Nach dem Lösen zeigen die Schlupfvariablen mit Wert > 0 präzise, welche Bedingung wo und um wie viel verletzt werden musste. Das ist zugleich die produktionstaugliche Lösung (Muster B18).
-
Schritt 4 — Zeitfenster und Erreichbarkeit prüfen. Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?
+
Schritt 4 — Zeitfenster und Erreichbarkeit prüfen. Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?
for kunde, (frueh, spaet) inenumerate(zeitfenster):assert distanz[depot][kunde] <= spaet, f"Kunde {kunde} nicht rechtzeitig erreichbar"
-
Schritt 5 — Rundungs- und Einheitenfehler. Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: sum(w) == 1 mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
+
Schritt 5 — Rundungs- und Einheitenfehler. Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: sum(w) == 1 mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen
Warum Schritt 2 so oft ins Leere läuft. Bedingungen einzeln abzuschalten funktioniert nur, solange es einen Schuldigen gibt. Überlagern sich zwei unabhängige Widersprüche, bleibt das Modell nach jeder einzelnen Abschaltung unlösbar — die Suche endet mit null Treffern, obwohl beide Widersprüche unverändert im Modell stehen. Genau dann steht man vor einem Modell mit tausenden Restriktionen und einem Statuscode.
Der Deletion Filter dreht die Frage um. Er fragt nicht „ist diese Bedingung schuld?“, sondern „wird diese Bedingung für den Widerspruch überhaupt gebraucht?“ — und das ist eine Frage, die sich beantworten lässt:
Nimm eine Bedingung versuchsweise heraus. Bleibt der Rest unlösbar, wurde sie nicht gebraucht: weg damit, endgültig. Wird der Rest lösbar, war sie beteiligt: sie bleibt.
-
Nach genau einem Durchlauf über alle n Bedingungen ist die übrig gebliebene Menge unreduzierbar — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein Irreducible Infeasible Subset, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion an (computeIIS bei Gurobi, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet n Aufrufe des Solvers statt 2^n durchprobierter Teilmengen.
+
Nach genau einem Durchlauf über alle n Bedingungen ist die übrig gebliebene Menge unreduzierbar — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein Irreducible Infeasible Subset, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion an (computeIIS bei Gurobi, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet n Aufrufe des Solvers statt 2^n durchprobierter Teilmengen.
#!/usr/bin/env python3# Konfliktsuche.py
@@ -530,7 +530,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
====================================================================
Vier Dinge sind daran wichtig.
-
Die Zulässigkeitsprüfung braucht keine Zielfunktion.linprog bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
+
Die Zulässigkeitsprüfung braucht keine Zielfunktion.linprog bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
Gefiltert werden nur die aufgeführten Bedingungen, nicht die Variablenschranken. Die Nichtnegativität steht in bounds und bleibt in jedem Teilmodell stehen. Sitzt Ihr Widerspruch in den Schranken (x >= 5 als Bound statt als Zeile), findet ihn der Filter nicht — schreiben Sie solche Grenzen dann als benannte Bedingung.
Sprechende Namen sind kein Luxus. Der Befund ist genau so brauchbar wie die Namen, die darin vorkommen. constraint_47, constraint_112, constraint_9 ist kein Befund.
Der Filter liefert einen kleinsten Konflikt, nicht den kleinsten. Dieses Modell mit vierzehn Bedingungen enthält neun verschiedene minimale Konflikte; welchen man zu sehen bekommt, entscheidet allein die Prüfreihenfolge. Das ist kein Mangel des Verfahrens, sondern eine Eigenschaft des Problems.
@@ -539,7 +539,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
⚠️ Was der Filter kostet Ein Konflikt kostet n Solverläufe. Bei 14 Bedingungen sind das 14 — bei 50 000 Restriktionen mit je zehn Sekunden Lösungszeit wären es knapp sechs Tage. Für große Modelle filtert man deshalb nicht einzeln, sondern gruppenweise: erst über Bedingungsblöcke (alle Kapazitäten, alle Verträge, alle Zeitfenster) laufen, dann den Filter nur noch innerhalb des einen Blocks anwenden, der übrig bleibt. Das ist derselbe Algorithmus auf einer gröberen Ebene und senkt die Zahl der Läufe um Größenordnungen.
-
Das Verhältnis zur Relaxation. Der Deletion Filter beantwortet die Frage „was widerspricht sich?“; die Schlupfvariablen aus Schritt 3 beantworten die Frage „was tun wir jetzt?“. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (Infeasibility_Diagnose.py, Abschnitt 22.3). Ein System, das im Ernstfall den Konflikt benennt und trotzdem einen Notfallplan liefert, hat beides.
+
Das Verhältnis zur Relaxation. Der Deletion Filter beantwortet die Frage „was widerspricht sich?“; die Schlupfvariablen aus Schritt 3 beantworten die Frage „was tun wir jetzt?“. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (Infeasibility_Diagnose.py, Abschnitt 22.3). Ein System, das im Ernstfall den Konflikt benennt und trotzdem einen Notfallplan liefert, hat beides.
🎯 Die eigentliche LehreINFEASIBLE in Produktion ist ein Entwurfsfehler, kein Betriebsfehler. Ein System, das im Ernstfall nur „geht nicht“ sagt, ist wertlos. Bauen Sie Schlupfvariablen von vornherein ein (Abschnitt 22.3).
@@ -567,9 +567,9 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
loesen(); t2 = time.perf_counter()print(f"Aufbau {t1-t0:.2f}s | Loesen {t2-t1:.2f}s")
Bei CVXPY und bei Schleifen über Szenarien ist oft der Aufbau der Engpass, nicht der Solver (siehe Abschnitt 20.6).
-
2. Big-M zu groß? Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie M auf die kleinste gültige Schranke (Abschnitt 6.5).
+
2. Big-M zu groß? Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie M auf die kleinste gültige Schranke (Abschnitt 6.5).
3. Symmetrie im Modell? Identische Maschinen, austauschbare Mitarbeitende, gleichwertige Fahrzeuge — der Solver durchsucht alle Vertauschungen. Abhilfe: Ordnungsbedingungen (Muster B20).
-
4. Falsche Solverfamilie? Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit scipy.optimize.minimize — jeweils Größenordnungen langsamer als das passende Werkzeug (Abschnitt 3.6).
+
4. Falsche Solverfamilie? Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit scipy.optimize.minimize — jeweils Größenordnungen langsamer als das passende Werkzeug (Abschnitt 3.6).
5. Wird vektorisiert?
# langsam: # schnell:for s inrange(S): constraints.append(u >=-(R @ w) - gamma)
@@ -616,9 +616,9 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
@@ -641,12 +641,12 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
print(f"Kondition {eig.max()/eig.min():.0f} | kleinster EW {eig.min():.2e}")# Faustregel: Kondition > 1000 ist bedenklich, > 10000 kritisch
Derselbe Fehler außerhalb der Finanzwelt
-
Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^7) und Tonnen-Angaben (10^{-3}) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
+
Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^7) und Tonnen-Angaben (10^{-3}) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")# Grobe Peilung: Wertebereiche der Koeffizienten anschauenprint(f"kleinster / groesster Betrag: {np.abs(A[A !=0]).min():.1e} "f"/ {np.abs(A).max():.1e}")
-
Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. Skalierung_Kondition.py zeigt beides samt Ruiz-Equilibrierung (Abschnitt 2.7).
+
Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. Skalierung_Kondition.py zeigt beides samt Ruiz-Equilibrierung (Abschnitt 2.7).
C6 — Falsche Dualwerte
Symptom. Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource.
@@ -654,16 +654,16 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
1. Vorzeichenkonvention. Haben Sie zur Maximierung negiert?
schattenpreise =-res.ineqlin.marginals # bei linprog nach Negation
Genau das ist die Vorzeichenfalle aus Abschnitt 5.7.
Dieser Test ist unabhängig von jeder Vorzeichenkonvention. Nutzen Sie ihn.
-
4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung. Die drei Prüfungen oben setzen voraus, dass es einen richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern mehrdeutig: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
+
4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung. Die drei Prüfungen oben setzen voraus, dass es einen richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern mehrdeutig: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
aktiv =int(np.sum(res.slack <1e-9)) # wie viele Bedingungen sind straff?if aktiv >len(res.x):print(f"Entartet: {aktiv} aktive Bedingungen bei {len(res.x)} Variablen.")
@@ -676,7 +676,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
C8 — DCPError
cvxpy.error.DCPError: Problem does not follow DCP rules.
-
Bedeutung. CVXPY kann die Konvexität Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. Das ist ein Feature.
+
Bedeutung. CVXPY kann die Konvexität Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. Das ist ein Feature.
@@ -694,7 +694,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Ursache.ortools und highspy bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen nicht im selben Prozess importieren (siehe Abschnitt 3.5). Der Konflikt entsteht auch indirekt: cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit — ein Skript, das erst cvxpy und dann ortools importiert, crasht daher mit derselben Meldung.
+
Ursache.ortools und highspy bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen nicht im selben Prozess importieren (siehe Abschnitt 3.5). Der Konflikt entsteht auch indirekt: cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit — ein Skript, das erst cvxpy und dann ortools importiert, crasht daher mit derselben Meldung.
Abhilfen (in dieser Reihenfolge): 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse — ein ProcessPoolExecutor mit mp_context="spawn" und max_tasks_per_child=1, siehe Ein_System_Vier_Ansaetze.py. 3. Auf highspy verzichten: HiGHS ist ohnehin Backend von scipy.optimize.linprog und CVXPY. 4. Getrennte virtuelle Umgebungen.
Nachschlagewerk zu allen Fachbegriffen, Abkürzungen, Algorithmen und Bibliotheken dieses Buchs. Jeder Eintrag nennt am Ende den Abschnitt, in dem der Begriff eingeführt wird — ein Klick dorthin führt in der Online-Fassung direkt zur Erklärung im Zusammenhang. Wer einen Begriff an allen Fundstellen sucht, benutzt das Stichwortverzeichnis.
A
+
Absolutbetrag — Modellierungsmuster für |x-z|: Der Betrag selbst ist nicht linear, lässt sich aber durch eine Hilfsvariable d mit den beiden Bedingungen x-z \le d und z-x \le d ersetzen. Das funktioniert nur, wenn d minimiert wird — sonst bleibt die Schranke unausgeschöpft. → Anhang B
+
+
Adjusted Close — Um Dividenden und Splits bereinigter Schlusskurs. Nur mit ihm sind Renditen über längere Zeiträume vergleichbar; der unbereinigte Schlusskurs erzeugt an jedem Ausschüttungstag einen Scheinverlust. → Abschnitt 18.3
+
+
Aktivierungsschalter — Modellierungsmuster, das eine kontinuierliche Größe an eine Binärvariable koppelt: x \le M\,y erlaubt x > 0 nur, wenn y = 1 ist. Grundbaustein für Fixkosten, Standortentscheidungen und Mindestmengen. → Anhang B
+
+
Almgren-Chriss-Modell — Standardmodell der optimalen Orderausführung. Es löst den Zielkonflikt zwischen Marktauswirkung bei schnellem Handeln und Volatilitätsrisiko bei langsamem Handeln und liefert einen Ausführungsplan über mehrere Perioden. → Abschnitt 13.4
+
+
Alternativoptima — Mehrere Lösungen mit identischem Zielfunktionswert. Sie treten auf, wenn die Zielfunktion parallel zu einer Kante des Polyeders verläuft oder die Matrix des quadratischen Terms nur semidefinit ist. Der Solver meldet dann eine beliebige davon — zwei Läufe können unterschiedlich aussehen und trotzdem beide optimal sein. → Abschnitt 11.3
+
+
Approximate Dynamic Programming — Sammelbegriff für Verfahren, die die Wertfunktion der dynamischen Programmierung nicht exakt tabellieren, sondern annähern — etwa durch Regression oder ein neuronales Netz. Antwort auf den Fluch der Dimensionalität, wenn der Zustandsraum zu groß zum Durchzählen wird. → Abschnitt 22.9
+
B
+
+
Backtest — Rückblickender Test einer Strategie auf historischen Daten. Ein Backtest ist nur so glaubwürdig wie seine Daten, seine Kostenannahme und seine Trennung von Trainings- und Testzeitraum — Kapitel 21 zeigt die fünf Selbsttäuschungen, die ihn wertlos machen. → Kapitel 21
+
+
Bedingte Kopplung — Modellierungsmuster für „wenn A und B, dann C“. Umgesetzt über y_C \ge y_A + y_B - 1: Nur wenn beide Voraussetzungen gesetzt sind, wird die Folge erzwungen. → Anhang B
+
+
Bellman-Gleichung — Rekursionsgleichung der dynamischen Programmierung: Der Wert eines Zustands ist die Summe aus den unmittelbaren Kosten der besten Aktion und dem Wert des Folgezustands. Sie zerlegt eine mehrstufige Entscheidung in eine Kette einstufiger. → Abschnitt 13.3
+
+
Benders-Zerlegung — Dekompositionsverfahren für Probleme mit wenigen „schweren“ und vielen „leichten“ Variablen. Ein Masterproblem entscheidet über die schweren, ein Subproblem über den Rest und schickt Schnitte (Benders-Cuts) zurück, bis beide übereinstimmen. → Abschnitt 22.9
+
+
Big-M-Methode — Modellierungstrick, der logische Bedingungen über eine hinreichend große Konstante M an eine Binärvariable koppelt. M sollte so klein wie möglich gewählt werden: Große Werte weichen die Relaxation auf, verschlechtern die Laufzeit und können zu Trickle Flow führen. → Abschnitt 6.5
+
+
Binärvariable — Entscheidungsvariable mit Wertebereich \{0,1\}. Sie schaltet Fixkosten, Kapazitäten oder logische Alternativen an und aus und macht aus einem linearen Programm ein gemischt-ganzzahliges. → Abschnitt 1.6
+
+
Bipartites Matching — Zuordnung zwischen zwei disjunkten Mengen — etwa Personen und Aufgaben — mit maximalem Nutzen oder minimalen Kosten. Klassisch gelöst durch den Ungarischen Algorithmus; die Modellmatrix ist total unimodular, weshalb schon das LP ganzzahlige Ecken hat. → Abschnitt 8.4
+
+
Black-Box-Effekt — Ablehnung eines Optimierungssystems, weil seine Entscheidungen nicht nachvollziehbar sind. Gegenmittel sind ein Constraint-Trace für Einzelentscheidungen und eine Kostenzerlegung für Bewertungen. Akzeptanz ist keine Nebensache, sondern Betriebsvoraussetzung. → Abschnitt 22.3
+
+
Branch-and-Bound — Exaktes Verfahren für ganzzahlige Probleme: Der Suchraum wird rekursiv zerlegt (Branching), und ein Zweig wird verworfen, sobald seine Relaxation schlechter ist als die beste bekannte Lösung (Bounding). Ohne das Abschneiden wäre es bloßes Durchprobieren. → Abschnitt 6.4
+
+
Branch-and-Cut — Kombination aus Branch-and-Bound und Schnittebenenverfahren: In den Knoten des Suchbaums werden zusätzlich gültige Ungleichungen erzeugt, die gebrochene Lösungen abschneiden. Der Standardalgorithmus heutiger MILP-Solver. → Abschnitt 3.3
+
+
Brute Force — Vollständiges Durchprobieren aller Möglichkeiten. Für kleine Instanzen ein nützlicher Kontrollwert, in der Praxis meist chancenlos: Schon 20 binäre Entscheidungen ergeben über eine Million Kombinationen. → Abschnitt 1.3
+
+
Budgeted Uncertainty — Robustheitsansatz nach Bertsimas und Sim: Höchstens \Gamma von n Parametern nehmen gleichzeitig ihren ungünstigsten Wert an. Der Parameter \Gamma regelt stufenlos, wie vorsichtig die Lösung wird — bei \Gamma = 0 nominal, bei \Gamma = n maximal pessimistisch. → Abschnitt 12.6
+
+
Budgetlimit — Nebenbedingung, die die Summe gewichteter Entscheidungen nach oben begrenzt. Der zugehörige Schattenpreis beantwortet unmittelbar, was eine zusätzliche Budgeteinheit einbrächte. → Anhang B
+
C
+
Calmar Ratio — Jahresrendite geteilt durch den Betrag des maximalen Drawdowns. Ergänzt die Sharpe Ratio um die Verlustperspektive: Sie misst Ertrag je Einheit des schlimmsten erlebten Rückschlags statt je Einheit Schwankung. → Abschnitt 21.5
+
+
CDCL — Conflict-Driven Clause Learning — das Suchprinzip moderner SAT-Solver. Aus jedem Widerspruch wird eine Sperrklausel abgeleitet, die verhindert, dass dieselbe Sackgasse erneut betreten wird; anschließend springt die Suche gezielt zurück statt Schritt für Schritt. → Abschnitt 7.3
+
+
Chance Constraint — Nebenbedingung, die nicht immer, sondern nur mit vorgegebener Wahrscheinlichkeit gelten muss — etwa „die Versorgung reicht in 95 % der Fälle“. Bei normalverteilter Unsicherheit wird daraus eine konische Bedingung, sonst hilft eine szenariobasierte Big-M-Formulierung. → Abschnitt 12.7
+
+
Column Generation — Verfahren für Modelle mit sehr vielen Variablen: Gerechnet wird auf einer kleinen Teilmenge, und ein Preisproblem sucht wiederholt die eine Spalte, die sich noch lohnt. Erst wenn keine mehr existiert, ist das Optimum bewiesen. → Abschnitt 9.8
+
+
Conditional Value at Risk (CVaR) — Erwarteter Verlust in den schlechtesten Szenarien jenseits des Value at Risk. Anders als der VaR ist der CVaR kohärent und subadditiv und lässt sich nach Rockafellar und Uryasev exakt als lineares Programm formulieren. → Abschnitt 20.4
+
+
Conflict Learning — Technik moderner SAT- und CP-Solver, aus jedem erkannten Widerspruch eine Sperrklausel abzuleiten. Der Solver lernt so aus Fehlschlägen, statt gleichartige Sackgassen immer wieder zu durchsuchen. → Abschnitt 7.3
+
+
Constraint Attribution — Diagnoseverfahren für den häufigsten Praxisfall: Das Modell rechnet, der Plan ist zulässig — und trotzdem enttäuschend. Jede Bedingungsgruppe wird einzeln gelockert und gemessen, wie sich der Zielwert ändert. Dasselbe Vorgehen wie der Deletion Filter, nur mit Kosten statt Zulässigkeit als Kriterium. → Abschnitt 22.4
+
+
Constraint Programming (CP) — Paradigma, das nicht über Zielfunktionsgradienten, sondern über logische Verträglichkeit sucht: Constraints schränken Wertebereiche ein, bis eine zulässige Belegung gefunden ist. Stark bei Reihenfolgen, Schichtplänen und Ressourcenkonflikten. → Abschnitt 7.3
+
+
Constraint Propagation — Kernmechanismus von CP-Solvern: Aus einer Zuweisung werden unmögliche Werte anderer Variablen sofort entfernt. Der Suchbaum schrumpft dadurch, bevor er überhaupt durchsucht wird. Genau hier liegt der Vorteil globaler Constraints — ihre Propagatoren schließen weit mehr Werte aus als die gleichwertige Menge einzelner Ungleichungen. → Abschnitt 7.3
+
+
Constraint-Trace — Nachvollziehbare Begründung einer einzelnen Solver-Entscheidung — „Warum bekommt Person A die Stunde und nicht Person B?“ — anhand der bindenden Bedingungen und Strafpunkte. Das Gegenmittel zum Black-Box-Effekt. → Abschnitt 22.3
+
+
CP-SAT — Constraint-Programming-Solver von OR-Tools. Er übersetzt ein Modell in boolesche Erfüllbarkeit und löst es mit Propagation, Conflict Learning und paralleler Suche. Für Reihenfolge- und Zuordnungsprobleme meist schneller als ein MILP-Solver. → Abschnitt 7.3
+
+
CP-SAT-Solver — Die Solver-Komponente von OR-Tools, angesprochen über cp_model.CpSolver(). Sie liefert neben der Lösung Statuscode, Laufzeit und die beste bekannte Schranke und lässt sich über num_workers auf mehrere Kerne verteilen. → Abschnitt 7.3
+
+
CSR-Format — Compressed Sparse Row — Speicherformat für dünn besetzte Matrizen, das nur die von null verschiedenen Einträge samt ihrer Positionen ablegt. Für Optimierungsmodelle mit zehntausenden Nebenbedingungen der Unterschied zwischen passt und passt nicht in den Speicher. → Abschnitt 3.5
+
+
CVRPTW — Capacitated Vehicle Routing Problem with Time Windows — Tourenplanung mit einer Flotte, Kapazitätsgrenzen je Fahrzeug und Zeitfenstern je Kunde. Die praxisnahe Variante des VRP und Standardfall der Routing-Bibliothek von OR-Tools. → Abschnitt 8.5
+
+
CVXPY — Modellierungssprache für konvexe Optimierung. Sie schreibt das Problem in mathematiknaher Notation, prüft die Konvexität nach den Regeln der Disciplined Convex Programming und reicht es an einen passenden Solver weiter. → Abschnitt 3.4
+
D
+
Data Snooping — Wiederholtes Ausprobieren von Strategien auf denselben Daten, bis eine gut aussieht. Der Fund ist dann eine Eigenschaft der Stichprobe, nicht des Marktes — gefährlicher als ein Programmierfehler, weil das Ergebnis plausibel wirkt. → Abschnitt 21.6
+
+
+
DCP (Disciplined Convex Programming) — Regelwerk von CVXPY, das jede Variable als konkav, konvex oder affin ausweist und aus der Kombination der Bausteine die Konkavität/Konvexität der Zielfunktion und der Nebenbedingungen erschließt. Kapitel 11 erklärt es; ein DCPError meldet eine Verletzung. → Kapitel 11
+
+
DCPError — Fehlermeldung von CVXPY, wenn ein Ausdruck die Regeln der Disciplined Convex Programming verletzt. Sie besagt nicht, dass das Problem unlösbar ist, sondern dass CVXPY die Konvexität nicht nachweisen kann — meist hilft eine äquivalente Umformung. → Anhang C
+
+
Deletion Filter — Verfahren zur Eingrenzung von Unlösbarkeit. Es fragt nicht „ist diese Bedingung schuld?“, sondern „wird diese Bedingung für den Widerspruch überhaupt gebraucht?“, und findet dadurch auch dann eine minimale unlösbare Teilmenge, wenn sich mehrere Widersprüche überlagern. → Anhang C
+
+
Diskrete Rendite — Einfache Rendite (P_t - P_{t-1})/P_{t-1}. Sie ist über Titel additiv und deshalb die richtige Wahl für Portfoliogewichte — anders als die logarithmische Rendite, die über die Zeit additiv ist. → Abschnitt 18.3
+
+
Diversifikation — Risikoreduktion durch Verteilung auf Titel, deren Renditen nicht vollständig gleichlaufen. Der Effekt steckt vollständig in den Kovarianzen: Bei Korrelation 1 verschwindet er, bei negativer Korrelation ist er am größten. → Abschnitt 19.3
+
+
DTO (Data Transfer Object) — Schlichtes Objekt, das nur Daten transportiert und keine Logik enthält. Im Buch trägt die Loesung-Klasse in or_kern.py das Ergebnis eines Solverlaufs so aus der Rechenschicht heraus. → Abschnitt 3.5
+
+
Dual Simplex — Variante des Simplex-Verfahrens, die eine dual zulässige Basis beibehält und die primale Zulässigkeit herstellt. Das Verfahren der Wahl beim Warm-Start, etwa in jedem Knoten eines Branch-and-Bound-Baums. → Abschnitt 3.3
+
+
Duales Problem — Das jedem Optimierungsproblem zugeordnete Gegenstück, dessen Variablen die Nebenbedingungen des Originals bepreisen. Seine Optimalwerte sind die Schattenpreise; im Optimum stimmen beide Zielfunktionswerte überein. → Abschnitt 5.6
+
+
Dualitätstheorie — Theorie des Zusammenhangs zwischen einem Optimierungsproblem und seinem Dualproblem. Der schwache Dualitätssatz liefert stets eine Schranke, der starke besagt, dass bei linearen Programmen beide Optima zusammenfallen. → Abschnitt 5.6
+
+
Dynamische Programmierung (DP) — Lösungsprinzip für mehrstufige Entscheidungen: Das Problem wird in Zustände und Stufen zerlegt und rückwärts gelöst, wobei jeder Zustand nur einmal bewertet wird. Voraussetzung ist das Optimalitätsprinzip. → Abschnitt 13.3
+
E
+
Ecke (Extrempunkt) — Punkt des zulässigen Bereichs, der sich nicht als echte Mischung zweier anderer zulässiger Punkte schreiben lässt. Nach dem Fundamentalsatz der linearen Optimierung liegt in einer Ecke stets ein Optimum — deshalb sucht der Simplex nur dort. → Abschnitt 2.4
+
+
Effizienzgrenze (Efficient Frontier) — Kurve aller Portfolios, die zu gegebenem Risiko die höchste erwartete Rendite liefern. Portfolios darunter sind dominiert, Portfolios darüber unerreichbar. → Abschnitt 19.4
+
+
Entartung — Zustand, in dem mehr Nebenbedingungen aktiv sind, als Variablen frei bleiben. Der Simplex kann dann Basiswechsel ohne Zielwertverbesserung durchführen, und die Schattenpreise sind nicht mehr eindeutig — zwei Solver dürfen verschiedene, gleichermaßen korrekte Dualwerte melden. → Abschnitt 5.9
+
+
Entscheidungsvariable — Die vom Solver frei wählbare Größe eines Modells. Ihr Wertebereich — kontinuierlich, ganzzahlig oder binär — bestimmt die Problemklasse und damit den Lösungsaufwand. → Abschnitt 1.6
+
+
Entscheidungsvektor — Zusammenfassung aller Entscheidungsvariablen zu einem Vektor \mathbf{x}. Erst diese Schreibweise macht die kompakte Matrixform eines Modells möglich. → Abschnitt 2.3
+
+
Entweder-Oder-Bedingung — Modellierungsmuster für sich ausschließende Alternativen: Genau eine von mehreren Bedingungen soll gelten. Umgesetzt über Binärvariablen mit Summe 1 und Big-M-Kopplung an die jeweilige Bedingung. → Anhang B
+
+
Error-Maximizer-Effekt — Eigenschaft der Mean-Variance-Optimierung, Schätzfehler zu verstärken statt auszugleichen. Der Optimierer sucht gezielt die Richtungen mit der niedrigsten geschätzten Varianz — und das sind genau jene, deren Varianz am stärksten unterschätzt wurde. → Abschnitt 18.4
+
+
EVPI (Expected Value of Perfect Information) — Differenz zwischen den erwarteten Kosten unter Unsicherheit und den Kosten bei perfektem Wissen. Obergrenze für den Wert jeder Prognoseverbesserung: Keine noch so gute Vorhersage kann mehr einbringen. → Abschnitt 12.5
+
+
Exklusiv-Oder — Modellierungsmuster für „genau eine von zwei Optionen“: y_A + y_B = 1. Die Abgrenzung zum einfachen Oder (y_A + y_B \ge 1) ist in der Praxis eine häufige Fehlerquelle. → Anhang B
+
+
Expected Shortfall — Anderer Name für den Conditional Value at Risk; in der Bankenregulierung die gebräuchliche Bezeichnung. Beide meinen den erwarteten Verlust jenseits des VaR-Quantils. → Abschnitt 20.4
+
+
Explainable OR — Nachvollziehbarmachung von Solver-Ergebnissen über Schattenpreise, aktive Restriktionen, Constraint-Trace und Kostenzerlegung. Das Gegenstück zu Explainable AI für Optimierungsmodelle. → Abschnitt 22.3
+
F
+
Fallback-Strategie — Festgelegtes Vorgehen für den Fall, dass der Solver kein Ergebnis liefert: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Niemals: nichts. → Abschnitt 22.5
+
+
Falsche Dualwerte — Fehlerbild, bei dem Schattenpreise unplausibel wirken. Zwei Ursachen dominieren: Entartung macht sie mehrdeutig, und ganzzahlige Modelle haben streng genommen gar keine Dualwerte — was ein MILP-Solver zurückgibt, gehört zur Relaxation des letzten Knotens. → Anhang C
+
+
Fat Tails — Verteilungsränder, die dicker auslaufen als bei der Normalverteilung. Extremereignisse sind dann deutlich häufiger als das Normalmodell vorhersagt — der Grund, warum varianzbasierte Risikomaße das Verlustrisiko unterschätzen. → Abschnitt 20.3
+
+
Fluch der Dimensionalität — Exponentielles Wachstum des Zustandsraums mit jeder zusätzlichen Zustandsdimension. Es begrenzt die dynamische Programmierung und motiviert näherungsweise Verfahren. → Abschnitt 13.5
+
+
Fluch des Durchschnitts (Flaw of Averages) — Systematischer Fehler beim Planen mit Erwartungswerten statt mit Verteilungen. Das Ergebnis der Durchschnittsrechnung ist im Allgemeinen nicht der Durchschnitt der Ergebnisse; formal folgt das aus der Jensenschen Ungleichung. → Abschnitt 12.3
+
+
Flusserhaltung — Bedingung, dass an jedem Knoten Abfluss minus Zufluss dem Saldo des Knotens entspricht — das Kirchhoff-Gesetz der Netzwerkoptimierung. Summieren sich die Salden nicht zu null, ist das Problem grundsätzlich unlösbar. → Abschnitt 8.3
+
+
Fundamentalsatz der linearen Optimierung — Das Optimum eines lösbaren linearen Programms wird stets in mindestens einer Ecke des zulässigen Polyeders angenommen. Die Rechtfertigung dafür, nur endlich viele Ecken statt unendlich vieler Punkte zu prüfen. → Abschnitt 2.4
+
G
+
Gemischt-ganzzahlige Optimierung (MILP) — Mixed-Integer Linear Programming — lineares Modell mit mindestens einer ganzzahligen oder binären Variablen. Die Ganzzahligkeit macht das Problem NP-schwer; gelöst wird es mit Branch-and-Cut. → Abschnitt 6.3
+
+
Genetischer Algorithmus — Metaheuristik, die eine Population von Lösungen durch Selektion, Rekombination und Mutation weiterentwickelt. Sie gibt keine Optimalitätsgarantie, kommt dafür mit Zielfunktionen zurecht, die weder linear noch differenzierbar sind. → Abschnitt 9.8
+
+
Gestaffelte Preise — Modellierungsmuster für Mengenrabatte und Tarifstufen. Die Menge wird in Segmente zerlegt, jedes mit eigenem Preis; Binärvariablen erzwingen, dass ein Segment erst genutzt wird, wenn das vorhergehende ausgeschöpft ist. Ohne diese Reihenfolgebedingung sucht sich der Solver einfach das billigste Segment heraus. → Anhang B
+
+
GIL (Global Interpreter Lock) — Sperre des CPython-Interpreters, die verhindert, dass zwei Threads gleichzeitig Python-Code ausführen. Sie ist der Grund, warum echte Parallelität in Python über Prozesse statt Threads läuft — Solver umgehen sie, weil sie in C++ rechnen. → Notation und Abkürzungen
+
+
Gini-Koeffizient — Maß für die Ungleichverteilung einer Größe zwischen 0 (alle gleich) und 1 (einer bekommt alles). Im Buch als Fairnesskennzahl für Wunscherfüllungsquoten verwendet. → Projektwerkstatt
+
+
Gleitendes Fenster — Auswertung, bei der ein Zeitfenster fester Länge über die Daten geschoben wird. Grundlage rollierender Schätzungen und des Walk-Forward-Backtests. → Anhang B
+
+
Global Minimum Variance Portfolio (GMV) — Portfolio kleinstmöglicher Varianz. Es benötigt keine Renditeprognose und ist deshalb deutlich robuster gegen Schätzfehler als das renditemaximierende Mean-Variance-Portfolio. → Abschnitt 19.4
+
+
Globale Constraints — Vorgefertigte Bausteine wie AllDifferent, NoOverlap oder Cumulative, die häufige Strukturen kompakt ausdrücken. Ihr eigentlicher Wert sind die spezialisierten Propagatoren, die weit mehr Werte ausschließen als die gleichwertige Menge einzelner Ungleichungen. → Abschnitt 7.4
+
+
Globales Minimum — Der kleinste Zielfunktionswert im gesamten zulässigen Bereich. Bei konvexen Problemen ist jedes lokale Minimum zugleich global — der Grund, warum Konvexität die Grenze zwischen leicht und schwer markiert. → Abschnitt 2.5
+
+
GLOP — Google Linear Optimization Package — der LP-Solver in OR-Tools. Er löst rein kontinuierliche Probleme; sobald Ganzzahligkeit ins Spiel kommt, übernimmt CP-SAT oder ein MILP-Backend. → Notation und Abkürzungen
+
+
Goldene Regel des Backtestens — Zum Zeitpunkt t darf nur Information verwendet werden, die zu t vorlag. Der Selbsttest mit absichtlich manipulierter Zukunft prüft das automatisch: Wird der Backtest dadurch besser, liegt ein Lookahead-Bias vor. → Abschnitt 21.12
+
+
Gradient — Vektor der partiellen Ableitungen einer Funktion; er zeigt in Richtung des steilsten Anstiegs. Gradientenbasierte Verfahren wie SLSQP nutzen ihn, um sich schrittweise an ein Optimum heranzutasten. → Abschnitt 11.2
+
+
+
Graphen — Mathematische Struktur aus Knoten und Kanten; das Modellierungsmittel für Netzwerke, Touren und Flüsse. Kapitel 8 behandelt Min-Cost-Flow, Matching und das Vehicle Routing Problem als Graphanwendungen. → Kapitel 8
+
+
Gurobi — Kommerzieller Hochleistungssolver für LP, MILP und QP. Im Buch nicht vorausgesetzt, aber als Referenzpunkt genannt: Über die Modellierungsschicht lässt er sich meist durch Ändern eines einzigen Arguments einsetzen. → Abschnitt 6.4
+
H
+
Herfindahl-Index — Summe der quadrierten Anteile; Maß für Konzentration. Sein Kehrwert ist die effektive Titelzahl und beantwortet, auf wie viele gleich große Positionen ein Portfolio praktisch verteilt ist. → Projektwerkstatt
+
+
Hierarchische Relaxation — Gegenmittel gegen Unlösbarkeit im Betrieb: Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. Der Solver liefert dann immer einen Plan und zeigt zugleich präzise, wo es klemmt. → Abschnitt 22.3
+
+
HiGHS — Offener Hochleistungssolver für LP, MILP und QP. Backend von scipy.optimize.linprog und CVXPY, direkt ansprechbar über highspy. → Abschnitt 3.4
+
I
+
IIS (Irreducible Infeasible Subset) — Minimale Menge von Nebenbedingungen, die zusammen unlösbar sind, deren echte Teilmengen aber alle lösbar wären. Das präziseste Ergebnis, das eine Unlösbarkeitsdiagnose liefern kann. → Anhang C
+
+
Implikation — Modellierungsmuster für „wenn A, dann B“: y_B \ge y_A. Die Umkehrung gilt dabei ausdrücklich nicht — B darf auch ohne A gesetzt sein. → Anhang B
+
+
Importfehler zwischen ortools und highspy — Absturz beim Import beider Bibliotheken im selben Prozess: Sie bringen je eine eigene HiGHS-Kopie mit, deren C++-Symbole kollidieren. Abhilfe ist die Trennung in getrennte Prozesse — CVXPY zieht ein installiertes highspy dabei ungefragt mit hinein. → Anhang C
+
+
Infeasibility — Zustand eines Modells ohne jede zulässige Lösung. Im Betrieb über Schlupfvariablen mit hohem Strafgewicht aufzufangen, in der Diagnose über den Deletion Filter einzugrenzen. → Abschnitt 22.3
+
+
Instabile Lösung — Fehlerbild, bei dem winzige Datenänderungen die Lösung völlig umwerfen. Meist eine Folge schlechter Konditionierung; die Konditionszahl zeigt es an, die Ruiz-Equilibrierung behebt es. → Anhang C
+
+
Interior-Point-Verfahren — Lösungsverfahren, das sich durch das Innere des zulässigen Bereichs zum Optimum bewegt, statt Ecken abzulaufen. Bei sehr großen Modellen oft schneller als der Simplex, liefert dafür keine Basislösung und damit weniger brauchbare Sensitivitätsinformationen. → Abschnitt 3.3
+
+
Intervallvariable — Variable mit Start, Dauer und Ende, die in CP-SAT eine Aktivität beschreibt. Sie ist die Voraussetzung für Überschneidungsverbote und Ressourcenprofile. → Abschnitt 7.4
+
J
+
Jensensche Ungleichung — Für konvexe Funktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]). Die formale Grundlage des Fluchs des Durchschnitts: Mit Mittelwerten zu rechnen unterschätzt systematisch die erwarteten Kosten. → Abschnitt 12.3
+
+
Job-Shop-Scheduling — Ablaufplanung, bei der mehrere Aufträge in je eigener Reihenfolge über dieselben Maschinen laufen. Klassisches Anwendungsfeld von CP-SAT; Zielgröße ist meist der Makespan. → Abschnitt 7.6
+
K
+
Kanonische Standardform — Einheitliche Matrixschreibweise eines LP als \min \mathbf{c}^\top\mathbf{x} unter den Bedingungen \mathbf{A}\mathbf{x} \le \mathbf{b} und \mathbf{x} \ge 0. Jedes lineare Modell lässt sich darauf bringen — Solver erwarten genau diese Form. → Abschnitt 2.3
+
+
Kapazitätsvektor — Der Vektor \mathbf{b} der rechten Seiten in \mathbf{A}\mathbf{x} \le \mathbf{b}. Seine Einträge sind die verfügbaren Ressourcen, und die zugehörigen Schattenpreise sagen, was eine zusätzliche Einheit davon wert wäre. → Abschnitt 2.3
+
+
Kardinalitätsbeschränkung — Obergrenze für die Anzahl gleichzeitig aktiver Entscheidungen, formuliert über die Summe der zugehörigen Binärvariablen. Typisch für „höchstens zehn Titel im Depot“. → Abschnitt 6.5
+
+
Karush-Kuhn-Tucker-Bedingungen (KKT) — Notwendige Optimalitätsbedingungen restringierter Probleme: Stationarität, primale und duale Zulässigkeit sowie komplementärer Schlupf. Bei konvexen Problemen sind sie zugleich hinreichend und damit ein vollständiger Optimalitätsnachweis. → Abschnitt 11.4
+
+
Kohärentes Risikomaß — Risikomaß mit den vier Eigenschaften Monotonie, Subadditivität, positive Homogenität und Translationsinvarianz. Der CVaR erfüllt sie, der VaR nicht — dessen Verletzung der Subadditivität kann Diversifikation rechnerisch bestrafen. → Abschnitt 20.4
+
+
Kombinatorische Explosion — Überproportionales Wachstum der Zahl möglicher Lösungen mit der Problemgröße. Sie ist der Grund, warum systematisches Ausprobieren schon bei kleinen Instanzen aussichtslos wird. → Abschnitt 1.4
+
+
Komplementärer Schlupf — Bedingung s_i \cdot y_i = 0: Eine Nebenbedingung ist entweder aktiv (kein Schlupf) oder ihr Schattenpreis verschwindet. Wirtschaftlich gelesen: Nur knappe Ressourcen haben einen Preis. → Abschnitt 5.6
+
+
Konditionszahl — Verhältnis von größtem zu kleinstem Singulärwert einer Matrix. Sie misst, wie stark kleine Datenänderungen die Lösung verzerren können; hohe Werte kündigen numerische Probleme an, lange bevor der Solver etwas meldet. → Abschnitt 18.6
+
+
Konische Optimierung — Erweiterung der linearen Optimierung auf Kegelbedingungen. Der Second-Order Cone (SOCP) erfasst Normen und damit Risikoschranken, die semidefinite Variante (SDP) Bedingungen an Matrizen. Beide bleiben konvex und damit global lösbar. → Abschnitt 12.7
+
+
Konvexität — Eigenschaft, bei der jede Verbindungsstrecke zweier Punkte innerhalb der Menge bzw. unterhalb des Funktionsgraphen liegt. Bei konvexen Problemen ist jedes lokale Optimum global — die eigentliche Grenze zwischen leicht und schwer lösbar. → Abschnitt 2.5
+
+
Korn-Transformation — Umformung der nicht-konvexen Sharpe-Ratio-Maximierung in ein konvexes quadratisches Programm durch Homogenisierung. Sie macht aus einem Bruch eine Skalierung und damit aus einem unlösbaren ein lösbares Problem. → Abschnitt 19.4
+
+
Kostenzerlegung — Aufschlüsselung eines Zielfunktionswerts in seine Bestandteile — etwa Alpha-Ertrag, Risikoprämie und Transaktionskostenstrafe. Sie beantwortet die Frage „woraus besteht dieses Ergebnis?“ und ist ein Baustein der Erklärbarkeit. → Abschnitt 22.3
+
+
Kovarianzmatrix — Matrix der paarweisen Kovarianzen aller Titel. Sie steuert im Markowitz-Modell den Diversifikationseffekt und ist bei vielen Titeln und wenigen Beobachtungen notorisch schlecht konditioniert. → Abschnitt 18.4
+
+
Kumulative Ressource — Ressource mit begrenzter Kapazität, die mehrere Aktivitäten gleichzeitig nutzen können — etwa vier Monteure. In CP-SAT über AddCumulative ausgedrückt. → Anhang B
+
L
+
Lagrange-Funktion — Zielfunktion zuzüglich der mit Multiplikatoren gewichteten Nebenbedingungen. Ihre Stationaritätsbedingung ist die erste der KKT-Bedingungen und die Brücke zwischen restringierter und freier Optimierung. → Abschnitt 11.4
+
+
Lagrange-Multiplikator — Gewicht, mit dem eine Nebenbedingung in die Lagrange-Funktion eingeht. Sein Optimalwert ist der Schattenpreis der Bedingung. → Abschnitt 11.4
+
+
Large Neighborhood Search (LNS) — Metaheuristik, die wiederholt einen größeren Teil der aktuellen Lösung zerstört und exakt neu aufbaut. Sie verbindet die Reichweite heuristischer Sprünge mit der Genauigkeit eines Solvers auf dem Teilproblem. → Abschnitt 9.7
+
+
Laufzeitexplosion — Fehlerbild, bei dem ein Modell nach einer kleinen Erweiterung nicht mehr fertig rechnet. Häufige Ursachen sind ein zu großes M, fehlende Symmetriebrechung und eine schwache Relaxation. → Anhang C
+
+
Ledoit-Wolf-Shrinkage — Schrumpfung der Stichprobenkovarianz in Richtung eines strukturierten Ziels; der optimale Mischungsfaktor wird analytisch bestimmt. scikit-learn verwendet die skalierte Einheitsmatrix als Ziel. → Abschnitt 18.5
+
+
Lexikografische Optimierung — Mehrzielverfahren mit strenger Rangfolge: Das wichtigste Ziel wird optimiert, sein Wert festgeschrieben, dann das zweitwichtigste — und so fort. Kein Ziel darf zugunsten eines nachrangigen verschlechtert werden. → Abschnitt 14.7
+
+
Lineare Programmierung (LP) — Linear Programming — Optimierung einer linearen Zielfunktion unter linearen Nebenbedingungen mit kontinuierlichen Variablen. Die am besten verstandene Problemklasse des Operations Research: in Polynomialzeit lösbar und mit vollständiger Dualitätstheorie. → Abschnitt 5.3
+
+
Linopy — Modellierungsbibliothek, die Optimierungsmodelle über beschriftete Achsen aufbaut (xarray-Stil). Ihre Stärke sind große, regelmäßig indizierte Modelle wie Energiesystemrechnungen. → Abschnitt 3.7
+
+
linprog — Funktion scipy.optimize.linprog zum Lösen linearer Programme. Sie erwartet das Modell in kanonischer Standardform als Matrizen und benutzt seit SciPy 1.11 standardmäßig HiGHS als Backend. → Abschnitt 5.7
+
+
Logarithmische Rendite — Stetige Rendite als Logarithmus des Preisverhältnisses. Sie ist über die Zeit additiv und deshalb die richtige Wahl für Zeitreihenaggregation — nicht aber für die Gewichtung im Portfolio. → Abschnitt 18.3
+
+
Lokales Minimum — Punkt, der in seiner Umgebung optimal ist, global aber übertroffen werden kann. Bei nichtkonvexen Problemen liefern gradientenbasierte Verfahren nur solche Punkte; ein Multistart macht das Problem sichtbar. → Abschnitt 2.5
+
+
Lookahead-Bias — Fehler, bei dem Informationen in eine Entscheidung einfließen, die zum Entscheidungszeitpunkt noch nicht vorlagen. Die häufigste Ursache für Backtests, die zu gut aussehen, um wahr zu sein. → Abschnitt 21.6
+
+
LP-Relaxation — Das ganzzahlige Modell ohne die Ganzzahligkeitsforderung. Ihre Lösung ist schnell zu berechnen und liefert eine Schranke: Besser als der Relaxationswert kann die ganzzahlige Lösung nicht werden. Genau darauf beruht das Abschneiden im Branch-and-Bound. → Abschnitt 6.4
+
M
+
Makespan — Gesamtdurchlaufzeit eines Ablaufplans: der Zeitpunkt, zu dem die letzte Aktivität endet. Standardzielgröße im Job-Shop-Scheduling. → Abschnitt 7.6
+
+
MAPE — Mean Absolute Percentage Error — mittlerer absoluter prozentualer Prognosefehler. Beliebt, weil einheitenfrei, aber unbrauchbar bei Werten nahe null. → Abschnitt 15.3
+
+
Marchenko-Pastur-Gesetz — Ergebnis der Zufallsmatrizentheorie über die Eigenwertverteilung von Stichproben-Kovarianzmatrizen. Es zeigt, dass die kleinsten Eigenwerte systematisch unterschätzt und die größten überschätzt werden — die theoretische Wurzel des Error-Maximizer-Effekts. → Abschnitt 18.4
+
+
Markowitz-Modell (Mean-Variance) — Rahmenwerk, das ein Portfolio über das Zusammenspiel von erwarteter Rendite und Kovarianz bewertet. Es begründete die moderne Portfoliotheorie und ist zugleich das Lehrbeispiel für die Empfindlichkeit gegenüber Schätzfehlern. → Abschnitt 19.4
+
+
Marktauswirkung (Market Impact) — Preisverschlechterung, die eine eigene Order durch ihr Volumen auslöst. Sie wächst überproportional mit der Handelsgeschwindigkeit und ist der Grund, große Orders über die Zeit zu strecken. → Abschnitt 13.4
+
+
Matplotlib — Standardbibliothek für statische Diagramme in Python. Im Buch erzeugt sie alle Abbildungen der Druckfassung; die interaktiven Fassungen der Website stammen von Plotly. → Abschnitt 3.4
+
+
Matrixform — Kompakte Schreibweise eines Modells über Vektoren und Matrizen statt einzelner Gleichungen. Sie ist nicht nur kürzer, sondern die Form, die Solver tatsächlich entgegennehmen. → Abschnitt 2.12
+
+
Matrix-Vektor-Produkt — Rechenoperation \mathbf{A}\mathbf{x}, die jede Zeile der Matrix mit dem Vektor multipliziert. In der Optimierung berechnet sie den Ressourcenverbrauch aller Nebenbedingungen in einem Schritt. → Abschnitt 2.3
+
+
Maximin-Fairness — Zielsetzung „der am schlechtesten Gestellte soll möglichst gut dastehen“. Modelliert über eine Hilfsvariable t mit t \le f_i(\mathbf{x}) für alle i und Maximierung von t. → Anhang B
+
+
Maximum Drawdown — Größter prozentualer Rückgang vom bisherigen Höchststand. Anders als die Volatilität misst er den tatsächlich erlittenen Verlustweg und ist deshalb die Kennzahl, an der Anleger aussteigen. → Abschnitt 21.5
+
+
Mensch in der Schleife — Betriebsprinzip, bei dem der Optimierer vorschlägt und ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab, bevor sie wirksam werden. → Abschnitt 22.5
+
+
Metaheuristik — Übergeordnetes Suchschema wie Simulated Annealing, Tabu-Suche oder genetische Algorithmen, das auf viele Problemtypen anwendbar ist. Es liefert gute Lösungen ohne Optimalitätsbeweis und ist dort angebracht, wo exakte Verfahren an der Größe scheitern. → Abschnitt 9.3
+
+
Min-Cost-Flow-Problem — Kostengünstigster Transport durch ein Netzwerk unter Kapazitäts- und Flusserhaltungsbedingungen. Die Modellmatrix ist total unimodular, weshalb schon die LP-Lösung ganzzahlig ausfällt. → Abschnitt 8.3
+
+
Mindestabnahmemenge — Bedingung „entweder gar nichts oder mindestens m“. Sie ist nicht linear und braucht eine Binärvariable: m\,y \le x \le M\,y. → Anhang B
+
+
MINLP — Mixed-Integer Nonlinear Programming — Modelle mit ganzzahligen Variablen und nichtlinearen Funktionen. Die schwierigste der Standardklassen: Sie vereint die kombinatorische Explosion mit dem Problem lokaler Optima. → Abschnitt 11.6
+
+
MIP-Gap — Relativer Abstand zwischen der besten gefundenen Lösung und der besten bekannten Schranke. Er beziffert, wie viel im schlechtesten Fall noch fehlt — ein Gap von 2 % heißt: höchstens 2 % besser ist überhaupt möglich. → Abschnitt 22.3
+
+
MIQP — Mixed-Integer Quadratic Programming — quadratische Zielfunktion mit ganzzahligen Variablen. Der Fall, der entsteht, sobald ein Portfoliomodell eine Obergrenze für die Zahl der Titel bekommt. → Notation und Abkürzungen
+
+
Monte-Carlo-Simulation — Erzeugung vieler Zufallsszenarien, um Kennzahlen empirisch zu schätzen. Sie bewertet eine gegebene Entscheidung, optimiert aber nicht — der Unterschied wird in der Praxis häufig übersehen. → Abschnitt 12.4
+
+
MSE — Mean Squared Error — mittlerer quadratischer Fehler. Das Standardmaß der Prognosegüte; es bestraft große Abweichungen überproportional und ist nicht dasselbe wie geringe Entscheidungskosten. → Notation und Abkürzungen
+
+
MTZ-Formulierung — Miller-Tucker-Zemlin-Bedingungen, die über Rangvariablen Kurzzyklen ausschließen. Kompakt in der Zahl der Nebenbedingungen, dafür mit schwacher Relaxation — der klassische Zielkonflikt bei Tourenmodellen. → Abschnitt 8.5
+
+
Multi-Objective Optimization — Optimierung mit mehreren, konkurrierenden Zielen. Statt einer besten Lösung gibt es eine Pareto-Front; die Auswahl daraus ist eine Entscheidung, keine Rechnung. → Abschnitt 22.9
+
N
+
Nebenbedingung (Constraint) — Gleichung oder Ungleichung, die zulässige von unzulässigen Lösungen trennt. Harte Bedingungen müssen erfüllt sein, weiche werden über Strafterme lediglich bepreist. → Abschnitt 1.6
+
+
Newsvendor-Problem — Klassisches Einperiodenmodell: Wie viel bestellt man bei unsicherer Nachfrage, wenn Überbestand und Fehlmenge unterschiedlich teuer sind? Die Lösung ist ein Quantil des Nachfragemodells, nicht ihr Erwartungswert. → Abschnitt 12.1
+
+
Nichtlineare Programmierung (NLP) — Nonlinear Programming — Problemklasse mit nichtlinearer Ziel- oder Nebenbedingungsfunktion. Ohne Konvexität liefern Verfahren wie SLSQP nur lokale Optima. → Abschnitt 11.5
+
+
Nichtüberlappung — Bedingung, dass sich Aktivitäten auf derselben Ressource zeitlich nicht überschneiden. In CP-SAT über AddNoOverlap auf Intervallvariablen ausgedrückt. → Anhang B
+
+
NP-schwer — Komplexitätsklasse von Problemen, für die kein Algorithmus mit polynomialer Laufzeit bekannt ist. Praktisch heißt das: Die Lösungszeit kann mit der Problemgröße explodieren — nicht aber, dass jede Instanz schwer wäre. → Abschnitt 6.4
+
+
NumPy — Grundbibliothek für numerisches Rechnen in Python. Sie liefert das Array, mit dem Vektoren und Matrizen eines Modells überhaupt erst kompakt und schnell aufgebaut werden können. → Abschnitt 3.4
+
O
+
Operations Research (OR) — Disziplin, die reale Entscheidungsprobleme in mathematische Modelle überführt und mit exakten oder heuristischen Algorithmen löst. Sie beantwortet nicht „was wird geschehen?“, sondern „was sollen wir tun?“. → Abschnitt 1.3
+
+
Optimalitätsprinzip — Grundsatz von Bellman: Jede Teilpolitik einer optimalen Politik ist ihrerseits optimal für den erreichten Zustand. Ohne ihn wäre die Rückwärtsinduktion nicht zulässig. → Abschnitt 13.3
+
+
OR-Tools — Optimierungssammlung von Google mit CP-SAT, einer Routing-Bibliothek und Anbindungen an LP/MILP-Solver. Im Buch die erste Wahl für Reihenfolge-, Zuordnungs- und Tourenprobleme. → Abschnitt 1.2
+
+
Overfitting — Anpassung eines Modells an Zufälligkeiten der Trainingsdaten statt an die zugrunde liegende Struktur. Im Backtesting die Regel, nicht die Ausnahme — deshalb der Walk-Forward-Test. → Abschnitt 21.6
+
P
+
pandas — Bibliothek für tabellarische Daten mit Zeit- und Beschriftungsindex. Im Buch der übliche Weg von Roh- zu Modelldaten, besonders in den Finanzkapiteln. → Abschnitt 3.4
+
+
Parameter — Feststehende Größe eines Modells — Kosten, Kapazitäten, Bedarfe. Parameter werden nicht optimiert, sondern gegeben; ihre Unsicherheit ist der Gegenstand der stochastischen und robusten Optimierung. → Abschnitt 1.6
+
+
Pareto-Front — Menge aller Lösungen, bei denen kein Ziel verbessert werden kann, ohne ein anderes zu verschlechtern. Sie zeigt den Zielkonflikt als Kurve, statt ihn in einer Gewichtung zu verstecken. → Abschnitt 14.3
+
+
Pivotisierung — Basiswechsel im Simplex-Verfahren: Eine Variable verlässt die Basis, eine andere tritt ein. Jeder Pivotschritt entspricht dem Weiterwandern zur benachbarten Ecke des Polyeders. → Abschnitt 5.4
+
+
Plotly — Bibliothek für interaktive Diagramme im Browser. Die Website des Buchs bindet damit Gantt-Diagramme, Effizienzlinien und Pareto-Fronten zum Zoomen und Erkunden ein. → Abschnitt 3.4
+
+
Polars — Schnelle DataFrame-Bibliothek mit spaltenorientiertem Speicher und verzögerter Auswertung. Im Buch als Alternative zu pandas dort erwähnt, wo der Modellaufbau selbst zum Engpass wird. → Abschnitt 3.8
+
+
Polyeder — Schnittmenge endlich vieler Halbräume — die geometrische Gestalt des zulässigen Bereichs eines linearen Programms. Seine Ecken sind die Kandidaten für das Optimum. → Abschnitt 2.4
+
+
Positiv (semi-)definit — Eigenschaft einer symmetrischen Matrix, deren Eigenwerte alle > 0 (definit) bzw. \ge 0 (semidefinit) sind. Definit bedeutet streng konvex und eine eindeutige Lösung, semidefinit konvex mit möglicherweise mehreren. → Abschnitt 11.3
+
+
Präskriptive Analytik — Analysestufe, die vorschreibt, welche Handlung unter den gegebenen Bedingungen die beste ist. Sie folgt auf die deskriptive (was war?) und die prädiktive Stufe (was wird?). → Abschnitt 1.3
+
+
Predict-then-Optimize — Zweistufiges Vorgehen: erst prognostizieren, dann mit der Prognose optimieren. Der Bruch liegt dazwischen — das Prognosemodell minimiert Vorhersagefehler, nicht Entscheidungskosten. → Abschnitt 15.3
+
+
Primales Problem — Das ursprünglich formulierte Optimierungsproblem, im Gegensatz zu seinem Dualproblem. Welches von beiden „primal“ heißt, ist Konvention: Das Duale des Dualen ist wieder das Primale. → Abschnitt 5.6
+
+
pydantic — Bibliothek zur Validierung von Datenstrukturen anhand von Typannotationen. Im Buch prüft sie die Eingaben des Optimierungsdienstes, bevor ein Modell überhaupt gebaut wird. → Abschnitt 3.4
+
+
pymoo — Bibliothek für mehrkriterielle Optimierung mit evolutionären Verfahren. Sie liefert Pareto-Fronten dort, wo sich die Ziele nicht sinnvoll skalarisieren lassen. → Abschnitt 3.4
+
+
Pyomo — Umfangreiche Modellierungssprache für Optimierung in Python, mit Unterstützung für abstrakte Modelle, viele Solver und nichtlineare Formulierungen. Mächtiger, aber ausführlicher als CVXPY. → Abschnitt 3.7
+
Q
+
Q-Learning — Verfahren des bestärkenden Lernens, das den Wert von Zustand-Aktion-Paaren aus Erfahrung schätzt, ohne ein Modell der Umgebung zu kennen. Verwandt mit der dynamischen Programmierung, aber lernend statt rechnend. → Abschnitt 13.5
+
+
Quadratische Programmierung (QP) — Quadratic Programming — Optimierung einer quadratischen Zielfunktion unter linearen Nebenbedingungen. Bei positiv semidefiniter Matrix konvex und damit zuverlässig global lösbar; die Portfoliooptimierung ist der Standardfall. → Abschnitt 11.3
+
R
+
Rebalancing — Periodische Rückführung des Portfolios auf die Zielgewichte. Zu häufig kostet Gebühren, zu selten lässt die Gewichte driften — und die Termine müssen Handelstage sein. → Abschnitt 21.4
+
+
Regime-Shift — Strukturbruch in den Daten, nach dem historisch geschätzte Momente ihre Gültigkeit verlieren. Kein Modellfehler, sondern eine Eigenschaft der Welt — abzufangen über kürzere Schätzfenster und robuste Formulierungen. → Abschnitt 22.3
+
+
Reinforcement Learning — Lernen einer Entscheidungsstrategie aus Rückmeldungen der Umgebung. Es löst dieselbe Klasse mehrstufiger Probleme wie die dynamische Programmierung, benötigt aber kein explizites Übergangsmodell. → Abschnitt 13.5
+
+
Relaxation — Absichtliches Weglassen einschränkender Forderungen — typischerweise der Ganzzahligkeit —, um eine schnell berechenbare Schranke zu gewinnen. Der Wert der Relaxation begrenzt, wie gut die echte Lösung überhaupt werden kann. → Abschnitt 6.4
+
+
Robuste Optimierung — Auslegung auf den ungünstigsten Fall innerhalb einer Unsicherheitsmenge. Sie verlangt keine Wahrscheinlichkeiten, sondern nur eine Angabe darüber, wie weit die Parameter schwanken können. → Abschnitt 12.6
+
+
Rockafellar-Uryasev-Theorem — Ergebnis, das die CVaR-Minimierung in eine konvexe Hilfsfunktion überführt. Es macht die Optimierung des Tail-Risikos mit gewöhnlichen linearen Solvern möglich. → Abschnitt 20.4
+
+
Routing-Bibliothek — Spezialisierter Teil von OR-Tools für Touren- und Reihenfolgeprobleme. Sie arbeitet heuristisch mit Zeitbudget — „optimal“ wird hier in der Regel nicht bewiesen. → Abschnitt 8.10
+
+
Rucksackproblem — Auswahl von Gegenständen mit Wert und Gewicht unter einer Kapazitätsgrenze. Das Schulbeispiel der ganzzahligen Optimierung und Modell für jede Budgetauswahl. → Abschnitt 6.6
+
+
Rückwärtsinduktion — Lösungsrichtung der dynamischen Programmierung: von der letzten Stufe rückwärts. Nur dort steht der Wert eines Zustands ohne Kenntnis der Zukunft fest, und von dort aus lässt sich jede frühere Stufe auf bereits bekannte Werte zurückführen. → Abschnitt 13.2
+
+
Ruiz-Equilibrierung — Skalierungsverfahren, das Zeilen und Spalten einer Matrix iterativ so multipliziert, dass alle Beträge in derselben Größenordnung liegen. Nach wenigen Durchläufen liegen die Maxima bei 1 — die einfachste wirksame Gegenmaßnahme gegen schlechte Konditionierung. → Abschnitt 2.7
+
+
Rüstzeit — Umschaltzeit zwischen zwei aufeinanderfolgenden Aufträgen auf derselben Maschine. Sie ist reihenfolgeabhängig und macht aus einer einfachen Zuordnung ein Reihenfolgeproblem. → Anhang B
+
S
+
SAT (Boolean Satisfiability) — Problem, für eine aussagenlogische Formel eine erfüllende Belegung zu finden. Das erste als NP-vollständig bewiesene Problem — und die Sprache, in die CP-SAT jedes Modell übersetzt. → Abschnitt 7.3
+
+
Satz von Birkhoff und von Neumann — Jede doppelt stochastische Matrix ist eine Mischung von Permutationsmatrizen. Er erklärt, warum das Zuordnungsproblem schon als lineares Programm ganzzahlige Lösungen liefert. → Abschnitt 8.4
+
+
Schattenpreis — Optimalwert einer Dualvariablen: Um wie viel ändert sich der Zielwert, wenn die zugehörige Ressource um eine Einheit erweitert wird? Er gilt nur lokal — innerhalb des Bereichs, den die Sensitivitätsanalyse ausweist. → Abschnitt 5.6
+
+
Schätzfehler — Abweichung geschätzter Größen wie Renditen oder Kovarianzen von ihren wahren Werten. In der Portfoliooptimierung nicht ein Randproblem, sondern das Hauptproblem. → Abschnitt 18.4
+
+
+
Scheduling — Zuweisung von Tätigkeiten an Maschinen oder Personen unter Ressourcen- und Reihenfolgebedingungen. Kapitel 7 behandelt es mit CP-SAT (Intervallvariablen), Kapitel 9 mit Simulated Annealing und LNS. → Kapitel 7
+
+
Schlupfvariable — Nichtnegative Hilfsvariable, die eine Ungleichung in eine Gleichung überführt. Ihr Wert zeigt die ungenutzte Reserve; ist er null, ist die Bedingung bindend. → Abschnitt 5.3
+
+
Schnittebenen (Cutting Planes) — Zusätzliche gültige Ungleichungen, die gebrochene LP-Lösungen abschneiden, ohne einen einzigen ganzzahligen Punkt zu verlieren. Sie verschärfen die Relaxation und verkleinern damit den Suchbaum. → Abschnitt 6.4
+
+
scikit-learn — Bibliothek für maschinelles Lernen. Im Buch für Prognosemodelle und für die Ledoit-Wolf-Shrinkage der Kovarianzmatrix eingesetzt. → Abschnitt 3.4
+
+
SCIP — Akademischer Solver für gemischt-ganzzahlige lineare und nichtlineare Programme, über OR-Tools und Pyomo ansprechbar. Für Forschung und Lehre frei nutzbar; kommerzieller Einsatz verlangt eine eigene Lizenz. → Abschnitt 6.4
+
+
SciPy — Wissenschaftliche Standardbibliothek von Python. Ihr Teilmodul scipy.optimize enthält mit linprog, milp und minimize die Einstiegswerkzeuge für lineare, ganzzahlige und nichtlineare Optimierung. → Abschnitt 3.4
+
+
Semikontinuierliche Variable — Variable, die entweder null ist oder in einem Intervall [m, M] liegt. Sie erfasst Mindestlosgrößen und wird über eine Binärvariable modelliert. → Abschnitt 6.5
+
+
Sensitivitätsanalyse — Untersuchung, in welchem Bereich sich Koeffizienten und Kapazitäten ändern dürfen, ohne die Struktur der Optimallösung zu verändern. Sie macht aus einer Zahl eine Aussage über ihre Belastbarkeit. → Abschnitt 5.8
+
+
Shannon-Entropie — Maß für die Gleichverteiltheit einer Verteilung. Als Zielfunktion eingesetzt erzwingt sie Streuung, ohne eine bestimmte Aufteilung vorzugeben. → Abschnitt 11.5
+
+
Sharpe Ratio — Überrendite über den risikofreien Zins je Einheit Volatilität. Ihre Maximierung ist zunächst nicht konvex, wird es aber durch die Korn-Transformation. → Abschnitt 19.4
+
+
Simplex-Algorithmus — Verfahren von Dantzig, das von Ecke zu Ecke des Polyeders wandert und den Zielfunktionswert dabei monoton verbessert. Im schlechtesten Fall exponentiell, in der Praxis außerordentlich zuverlässig. → Abschnitt 5.4
+
+
Simulated Annealing — Metaheuristik nach dem Vorbild des Metallabkühlens: Verschlechterungen werden anfangs häufig, später kaum noch akzeptiert. So entkommt die Suche lokalen Optima, ohne die Richtung ganz zu verlieren. → Abschnitt 9.5
+
+
Skalarisierung — Zusammenfassung mehrerer Ziele zu einer gewichteten Summe. Bequem, aber mit zwei Haken: Die Gewichte sind Werturteile, und nicht jeder Punkt der Pareto-Front lässt sich so erreichen. → Abschnitt 14.4
+
+
Skalarprodukt — Summe der paarweisen Produkte zweier Vektoren, \mathbf{c}^\top\mathbf{x}. In der Optimierung die kompakte Schreibweise jeder linearen Zielfunktion. → Abschnitt 2.3
+
+
SLSQP — Sequential Least Squares Programming — gradientenbasiertes Verfahren in scipy.optimize.minimize für nichtlineare Probleme mit Nebenbedingungen. Es findet lokale Optima; ohne Konvexität entscheidet der Startpunkt über das Ergebnis. → Abschnitt 11.5
+
+
Snapshot-Prinzip — Betriebsregel, nach der jeder Optimierungslauf auf einem unveränderlichen Datenschnappschuss mit eigener Kennung arbeitet. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar. → Abschnitt 22.5
+
+
+
Solver — Softwarekomponente, die ein mathematisches Modell löst — also das Optimierungsproblem in eine Lösung übersetzt. Kapitel 3 vergleicht die in diesem Buch verwendeten Solver (HiGHS, OR-Tools/CP-SAT, CVXPY) und ihre Stärken. → Kapitel 3
+
+
Solver-Status — Rückgabewert, mit dem ein Solver das Ergebnis einordnet: OPTIMAL (bewiesen optimal), FEASIBLE (zulässig, aber unbewiesen), INFEASIBLE (keine zulässige Lösung), UNBOUNDED (Zielwert unbeschränkt) und UNKNOWN (Abbruch ohne Aussage). Ihn ungeprüft zu übergehen ist die häufigste Fehlerquelle im produktiven Einsatz. → Notation und Abkürzungen
+
+
Starker Dualitätssatz — Aussage, dass primales und duales Problem im Optimum denselben Zielfunktionswert besitzen — bei linearen Programmen immer, sobald beide lösbar sind. Er macht den Dualwert zu einem belastbaren Preis statt zu einer bloßen Schranke. → Abschnitt 5.6
+
+
Stichproben-Kovarianzmatrix — Aus historischen Daten geschätzte Kovarianzmatrix. Bei weniger Beobachtungen als Titeln ist sie singulär und damit nicht invertierbar; schon bei ähnlicher Größenordnung wird sie extrem instabil. → Abschnitt 18.4
+
+
Stochastische Programmierung — Optimierung unter Unsicherheit mit bekannten Wahrscheinlichkeiten. In der zweistufigen Form wird zwischen Entscheidungen vor und nach der Beobachtung unterschieden — genau der Unterschied, den der Fluch des Durchschnitts einebnet. → Abschnitt 12.5
+
+
Strafkosten — Kosten, mit denen die Verletzung einer weichen Bedingung in der Zielfunktion belegt wird. Ihre Höhe ist eine Modellierungsentscheidung: Sie legt fest, wie viel Regelverstoß was wert ist. → Abschnitt 1.6
+
+
Subadditivität — Eigenschaft \rho(A+B) \le \rho(A)+\rho(B): Diversifikation darf das Risiko nicht erhöhen. Der VaR verletzt sie, weshalb er kein kohärentes Risikomaß ist. → Abschnitt 20.4
+
+
Subtour — Kurzzyklus, der nur einen Teil der Knoten enthält und das Depot umgeht. Ohne eigene Bedingungen — etwa MTZ — liefert ein Tourenmodell solche Gebilde als scheinbar optimale Lösung. → Abschnitt 8.5
+
+
Survivorship-Bias — Verzerrung durch die Auswahl nur derjenigen Titel, die es bis heute gibt. Die ausgeschiedenen fehlen — und mit ihnen die schlechtesten Verläufe. → Abschnitt 21.6
+
+
Symmetriebrechung — Zusätzliche Ordnungsbedingungen, die verhindern, dass der Solver gleichwertige Vertauschungen mehrfach durchsucht. Bei identischen Maschinen oder Fahrzeugen oft der größte einzelne Laufzeitgewinn. → Anhang B
+
T
+
Tabu-Suche — Metaheuristik, die zuletzt besuchte Lösungen für einige Schritte sperrt. Die Sperrliste zwingt die Suche aus lokalen Optima heraus, statt zwischen zwei Nachbarn zu pendeln. → Abschnitt 9.8
+
+
Tangentialportfolio — Portfolio auf der Effizienzgrenze mit der höchsten Sharpe Ratio — der Berührpunkt der Kapitalmarktlinie. Es reagiert besonders empfindlich auf Renditeschätzfehler. → Abschnitt 19.4
+
+
Technologiematrix — Die Matrix \mathbf{A} der Verbrauchskoeffizienten. Ihr Eintrag a_{ij} sagt, wie viel von Ressource i eine Einheit von Aktivität j verbraucht. Zusammen mit dem Kapazitätsvektor \mathbf{b} bildet sie die Nebenbedingungen \mathbf{A}\mathbf{x} \le \mathbf{b}. → Abschnitt 2.3
+
+
Totale Unimodularität — Eigenschaft einer Matrix, bei der jede quadratische Teilmatrix die Determinante 0, +1 oder -1 hat. Folge: Alle Ecken des Polyeders sind ganzzahlig — Ganzzahligkeit muss gar nicht gefordert werden. → Abschnitt 8.4
+
+
Transaktionskosten — Beim Umschichten anfallende Kosten aus Gebühren, Spread und Marktauswirkung. Über eine L_1-Strafe modellierbar, die zugleich den Turnover begrenzt. → Abschnitt 20.5
+
+
Trickle Flow — Fehlerbild bei zu großem M: Ein verschwindend kleiner Schaltwert — etwa 4 \cdot 10^{-8} — gilt dem Solver als null, lässt aber multipliziert mit einem riesigen M einen großen Fluss durch. Das Lager liefert, ohne offiziell geöffnet zu sein, und die Fixkosten fehlen in der Rechnung. → Abschnitt A.6
+
+
TSP (Traveling Salesperson Problem) — Suche nach der kürzesten Rundreise durch n Orte. Das bekannteste NP-schwere Problem und der Ausgangspunkt aller Tourenmodelle. → Abschnitt 8.5
+
+
Turnover (Umschlag) — Summe der absoluten Gewichtsänderungen einer Umschichtung. Die Größe, an der Transaktionskosten hängen — und die Kennzahl, mit der man Rebalancing-Regeln vergleicht. → Abschnitt 20.5
+
+
TWAP — Time-Weighted Average Price — Ausführungsstrategie, die eine Order gleichmäßig über die Zeit verteilt. Einfache Vergleichsgröße für jede optimierte Ausführung. → Abschnitt 13.4
+
U
+
Umrüstkosten — Kosten, die beim Wechsel zwischen zwei Produkten anfallen. Sie hängen vom Vorgänger ab und erzwingen deshalb Binärvariablen über Paare statt über einzelne Aufträge. → Anhang B
+
+
Unbounded — Zustand, in dem der Zielwert unbeschränkt verbessert werden kann. Fast immer ein Modellfehler — eine fehlende Kapazitätsgrenze oder ein vergessenes Vorzeichen —, nicht ein besonders gutes Ergebnis. → Anhang C
+
+
Ungarischer Algorithmus — Kombinatorisches Verfahren für das Zuordnungsproblem mit polynomialer Laufzeit. Es löst exakt, was als allgemeines MILP deutlich teurer wäre. → Abschnitt 8.10
+
+
Unsicherheitsmenge — Vorab definierter Bereich möglicher Parameterwerte, gegen dessen ungünstigstes Element eine robuste Lösung abgesichert wird. Ihre Größe steuert unmittelbar den Preis der Robustheit. → Abschnitt 12.6
+
+
Unsinniges Ergebnis — Fehlerbild, bei dem der Solver zwar OPTIMAL meldet, die Lösung aber offensichtlich unbrauchbar ist. Dann ist nicht die Rechnung falsch, sondern das Modell: Es beschreibt eine andere Wirklichkeit als die gemeinte. → Anhang C
+
V
+
Value at Risk (VaR) — Verlustschwelle, die mit vorgegebener Wahrscheinlichkeit nicht überschritten wird. Sie sagt nichts über die Verlusthöhe dahinter und ist nicht subadditiv — beides Gründe für den Übergang zum CVaR. → Abschnitt 20.4
+
+
Vehicle Routing Problem (VRP) — Verallgemeinerung des TSP auf mehrere Fahrzeuge mit Depot und Kapazitätsgrenzen. In der Variante mit Zeitfenstern (VRPTW) ist es der praktische Standardfall der Tourenplanung. Sich kreuzende Touren sind hier kein Fehler: Die Zeitfenster können die Reihenfolge erzwingen. → Abschnitt 8.5
+
+
Verdächtig guter Backtest — Fehlerbild der Finanzkapitel: eine Strategie, die im Rückblick zu gut abschneidet. Die Prüfreihenfolge lautet Lookahead, Survivorship, Kosten, Data Snooping — in dieser Reihenfolge, weil sie nach Häufigkeit sortiert ist. → Anhang C
+
+
Verhältnis-Bedingung — Anforderung der Art „höchstens 30 % aus einer Quelle“. Sie wird linear, sobald man sie als x_i \le 0{,}3 \sum_j x_j schreibt statt als Bruch. → Anhang B
+
+
Vertauschte Spalten — Fehlerbild, bei dem Daten und Modell unterschiedliche Reihenfolgen annehmen. Es rechnet widerspruchsfrei und liefert dennoch Unsinn — deshalb gehört die Spaltenreihenfolge abgesichert, nicht angenommen. → Anhang C
+
+
Volatilität — Standardabweichung der Renditen, üblicherweise auf ein Jahr skaliert. Sie misst Schwankung in beide Richtungen und behandelt Gewinne damit wie Verluste. → Abschnitt 18.3
+
+
Vorrangbeziehung — Bedingung „Aktivität A muss vor Aktivität B abgeschlossen sein“, formuliert als \text{Ende}_A \le \text{Start}_B. Der Grundbaustein jeder Ablaufplanung. → Anhang B
+
W
+
Walk-Forward-Backtest — Rollierende Auswertung, bei der Parameter stets nur auf Vergangenheitsdaten geschätzt und auf dem unmittelbar folgenden Zeitraum getestet werden. Die einzige Backtest-Bauform, die Lookahead strukturell ausschließt. → Abschnitt 21.5
+
+
Warm-Start — Übergabe einer bekannten Lösung als Startpunkt. Bei wiederholten, ähnlichen Läufen — etwa täglicher Neuplanung — oft der größte Zeitgewinn überhaupt. → Abschnitt 6.8
+
+
Weiche Grenze mit Strafkosten — Modellierungsmuster, das eine Schranke überschreitbar macht und die Überschreitung über eine Schlupfvariable in der Zielfunktion bepreist. So bleibt das Modell lösbar und zeigt trotzdem, wo es klemmt. → Anhang B
+
+
Widersprüchliche Solver — Fehlerbild, bei dem zwei Solver verschiedene Lösungen melden. Bei gleichem Zielwert sind es Alternativoptima und beide haben recht; bei verschiedenem Zielwert liegt ein Modell- oder Toleranzproblem vor. → Anhang C
+
+
Wurzel-Zeit-Regel — Skalierung der Volatilität mit \sqrt{T}. Sie gilt streng nur für Standardabweichungen unabhängiger Größen ohne Drift — für VaR und CVaR ist sie lediglich eine grobe Näherung. → Abschnitt 20.6
+
Y
+
yfinance — Bibliothek zum Abruf historischer Kursdaten. Die einzige Abhängigkeit des Buchs, die eine Internetverbindung braucht — vier Programme nutzen sie. → Abschnitt 3.4
+
Z
+
Zielfunktion — Der zu minimierende oder maximierende Ausdruck, der die Entscheidungsvariablen zu einer einzigen Bewertungszahl verdichtet. Ihre Formulierung entscheidet, was das Modell überhaupt für gut hält. → Abschnitt 1.6
+
+
Zufallsmatrizentheorie — Random Matrix Theory — Theorie der Eigenwertverteilung zufälliger Matrizen. Sie liefert den Maßstab dafür, welche Struktur in einer geschätzten Kovarianzmatrix echt ist und welche bloßes Rauschen. → Abschnitt 18.4
+
+
Zulässiger Bereich (Feasible Region) — Menge aller Punkte, die sämtliche Nebenbedingungen gleichzeitig erfüllen. Ist sie leer, ist das Modell unlösbar; ist sie unbeschränkt, kann der Zielwert entgleiten. → Anhang F
+
+
Zuordnungsproblem — Eins-zu-eins-Zuordnung zweier gleich großer Mengen mit minimalen Kosten. Dank totaler Unimodularität schon als LP ganzzahlig lösbar. → Anhang B
+
+
Zustandslosigkeit — Betriebsprinzip, nach dem der rechnende Dienst keinen Zustand hält: Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial. → Abschnitt 22.5
+
+
Zustandsraum — Menge aller Zustände, die ein mehrstufiges Entscheidungsproblem annehmen kann. Seine Größe entscheidet, ob dynamische Programmierung praktikabel ist. → Abschnitt 13.5
+
+
Zwei-Phasen-Simplex-Methode — Vorgehen, um überhaupt eine erste zulässige Ecke zu finden. Phase 1 minimiert mit Hilfsvariablen die Verletzung der Nebenbedingungen; bleibt dabei ein Rest, ist das Modell unlösbar. Erst Phase 2 optimiert die eigentliche Zielfunktion. → Abschnitt A.5
+
+
Zweischichtige Architektur — Trennung in eine Modellierungsschicht, in der man das Problem mathematiknah formuliert, und eine Solver-Schicht aus hochoptimierten C++-Bibliotheken. Man schreibt Optimierungsalgorithmen im modernen OR nicht selbst. → Abschnitt 3.3
★★★ Bertsimas, D. & Tsitsiklis, J. N. (1997):Introduction to Linear Optimization. Athena Scientific, Belmont. [Lineare Optimierung] Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, Sensitivitätsanalyse und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der Dualitätstheorie.
-
★★★ Nocedal, J. & Wright, S. J. (2006):Numerical Optimization (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und Ruiz-Equilibrierung.
+
★★★ Bertsimas, D. & Tsitsiklis, J. N. (1997):Introduction to Linear Optimization. Athena Scientific, Belmont. [Lineare Optimierung] Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, Sensitivitätsanalyse und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der Dualitätstheorie.
+
★★★ Nocedal, J. & Wright, S. J. (2006):Numerical Optimization (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und Ruiz-Equilibrierung.
★★ Hillier, F. S. & Lieberman, G. J. (2021):Introduction to Operations Research (11. Aufl.). McGraw-Hill, New York. [OR-Überblick] Breite Einführung inklusive Warteschlangen, Entscheidungsmodellen und Simulation — die Themen, die dieses Buch bewusst auslässt.
★★ Kallrath, J. (2021):Business Optimization Using Mathematical Programming (2. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-73237-0 [Modellierungspraxis] Der Brückenschlag von der betrieblichen Fragestellung zum Modell, mit ausführlichem Katalog industrieller Anwendungsfälle. Nächster Nachbar zu Kapitel 4.
-
★ Dantzig, G. B. (1963):Linear Programming and Extensions. Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] Das Originalwerk des Erfinders des Simplex-Algorithmus. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.
+
★ Dantzig, G. B. (1963):Linear Programming and Extensions. Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] Das Originalwerk des Erfinders des Simplex-Algorithmus. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.
Modellierung in Python: Bibliotheken und Modellierungsschichten
★★★ Diamond, S. & Boyd, S. (2016):CVXPY: A Python-embedded modeling language for convex optimization. In: Journal of Machine Learning Research 17(83), S. 1–5. [Bibliothek: CVXPY] Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. www.cvxpy.org
+
★★★ Diamond, S. & Boyd, S. (2016):CVXPY: A Python-embedded modeling language for convex optimization. In: Journal of Machine Learning Research 17(83), S. 1–5. [Bibliothek: CVXPY] Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. www.cvxpy.org
★★ Agrawal, A., Verschueren, R., Diamond, S. & Boyd, S. (2018):A rewriting system for convex optimization problems. In: Journal of Control and Decision 5(1), S. 42–60. DOI: 10.1080/23307706.2017.1397554 [Bibliothek: CVXPY] Erklärt, was CVXPY beim Übersetzen tatsächlich tut — und warum ein DCPError kein Solverfehler ist.
-
★★★ Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):Pyomo — Optimization Modeling in Python (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.
-
★★ Hofmann, F. (2023):Linopy: Linear optimization with n-dimensional labeled variables. In: Journal of Open Source Software 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.
-
★★ Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):SciPy 1.0: fundamental algorithms for scientific computing in Python. In: Nature Methods 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] Die zitierfähige Quelle zu scipy.optimize — dem Einstiegspunkt in Kapitel 5.
-
★★ Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):Array programming with NumPy. In: Nature 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.
+
★★★ Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):Pyomo — Optimization Modeling in Python (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.
+
★★ Hofmann, F. (2023):Linopy: Linear optimization with n-dimensional labeled variables. In: Journal of Open Source Software 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.
+
★★ Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):SciPy 1.0: fundamental algorithms for scientific computing in Python. In: Nature Methods 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] Die zitierfähige Quelle zu scipy.optimize — dem Einstiegspunkt in Kapitel 5.
+
★★ Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):Array programming with NumPy. In: Nature 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.
★★★ Wolsey, L. A. (2020):Integer Programming (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] Branch-and-Bound, Branch-and-Cut, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines M besser ist als ein großes.
+
★★★ Wolsey, L. A. (2020):Integer Programming (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] Branch-and-Bound, Branch-and-Cut, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines M besser ist als ein großes.
★★★ Conforti, M., Cornuéjols, G. & Zambelli, G. (2014):Integer Programming. Springer, Cham. DOI: 10.1007/978-3-319-11008-0 [Ganzzahlige Optimierung] Modernere Darstellung als Wolsey, mit ausführlicher Behandlung von Schnittebenen und totaler Unimodularität.
★★ Klotz, E. & Newman, A. M. (2013):Practical guidelines for solving difficult mixed integer linear programs. In: Surveys in Operations Research and Management Science 18(1–2), S. 18–32. DOI: 10.1016/j.sorms.2012.12.001 [Ganzzahlige Optimierung] Was zu tun ist, wenn ein Modell nicht fertig rechnet — Skalierung, Formulierung, Symmetrie. Die Literaturgrundlage von Anhang C.
-
★★ Achterberg, T. (2009):SCIP: solving constraint integer programs. In: Mathematical Programming Computation 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [Solver: SCIP] Beschreibt die Verbindung von MILP und Constraint Programming in einem Solver.
+
★★ Achterberg, T. (2009):SCIP: solving constraint integer programs. In: Mathematical Programming Computation 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [Solver: SCIP] Beschreibt die Verbindung von MILP und Constraint Programming in einem Solver.
Moderne Solver: Implementierung, Vergleich und Benchmarks
Zu Kapitel 3 und Anhang D. Neuer Abschnitt: Das Buch vergleicht durchgehend Solver, die Literatur dazu stand bisher verstreut.
-
★★★ Huangfu, Q. & Hall, J. A. J. (2018):Parallelizing the dual revised simplex method. In: Mathematical Programming Computation 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: HiGHS] Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von scipy.optimize.linprog und CVXPY. highs.dev
+
★★★ Huangfu, Q. & Hall, J. A. J. (2018):Parallelizing the dual revised simplex method. In: Mathematical Programming Computation 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: HiGHS] Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von scipy.optimize.linprog und CVXPY. highs.dev
★★★ Gleixner, A., Hendel, G., Gamrath, G. et al. (2021):MIPLIB 2017: data-driven compilation of the 6th mixed-integer programming library. In: Mathematical Programming Computation 13(3), S. 443–490. DOI: 10.1007/s12532-020-00194-3 [Benchmarks] Die Testmenge, auf der praktisch jeder Solververgleich beruht: 1065 Instanzen, davon 240 als Benchmark-Teilmenge. Frei zugänglich; Daten unter miplib.zib.de.
-
★★★ Mittelmann, H. D.:Benchmarks for Optimization Software. Arizona State University. plato.asu.edu/bench.html [Benchmarks] Die laufend aktualisierte Referenzmessung für LP-, MILP- und QP-Solver. Wichtige Einschränkung: Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich Gurobi gegen HiGHS ist dort nicht mehr möglich.
+
★★★ Mittelmann, H. D.:Benchmarks for Optimization Software. Arizona State University. plato.asu.edu/bench.html [Benchmarks] Die laufend aktualisierte Referenzmessung für LP-, MILP- und QP-Solver. Wichtige Einschränkung: Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich Gurobi gegen HiGHS ist dort nicht mehr möglich.
★★ Bolusani, S., Besançon, M., Bestuzheva, K. et al. (2024):The SCIP Optimization Suite 9.0. Technischer Bericht, Zuse-Institut Berlin. arXiv:2402.17702 [Solver: SCIP] Beschreibt Aufbau und Komponenten der Suite. Die Reihe wird fortgeschrieben — die jeweils aktuelle Fassung steht unter scipopt.org; beim Zitieren die tatsächlich benutzte Version angeben.
★★★ Rossi, F., van Beek, P. & Walsh, T. (Hrsg., 2006):Handbook of Constraint Programming. Foundations of Artificial Intelligence, Bd. 2. Elsevier, Amsterdam. [Constraint Programming] Nach wie vor das Referenzwerk zu Propagation, globalen Constraints und Suchstrategien.
-
★★★ Perron, L. & Furnon, V.:OR-Tools CP-SAT Solver Documentation. Google. developers.google.com/optimization/cp [Solver: CP-SAT] Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu AddNoOverlap und AddCumulative deckt sich mit Kapitel 7.
-
★★ Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):IBM ILOG CP Optimizer for scheduling. In: Constraints 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [Scheduling] Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.
+
★★★ Perron, L. & Furnon, V.:OR-Tools CP-SAT Solver Documentation. Google. developers.google.com/optimization/cp [Solver: CP-SAT] Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu AddNoOverlap und AddCumulative deckt sich mit Kapitel 7.
+
★★ Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):IBM ILOG CP Optimizer for scheduling. In: Constraints 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [Scheduling] Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.
★★ Biere, A., Heule, M., van Maaren, H. & Walsh, T. (Hrsg., 2021):Handbook of Satisfiability (2. Aufl.). Frontiers in Artificial Intelligence and Applications, Bd. 336. IOS Press, Amsterdam. DOI: 10.3233/FAIA336 [SAT] Die Grundlage dessen, was CP-SAT unter der Haube tut: Conflict-Driven Clause Learning, Klausellernen, Neustarts.
★★★ Ahuja, R. K., Magnanti, T. L. & Orlin, J. B. (1993):Network Flows: Theory, Algorithms, and Applications. Prentice Hall, Englewood Cliffs. [Netzwerkflüsse] Das Standardwerk zu Min-Cost-Flow, Matching und totaler Unimodularität — der Grund, warum diese Modelle schon als LP ganzzahlig lösen.
-
★★★ Toth, P. & Vigo, D. (Hrsg., 2014):Vehicle Routing: Problems, Methods, and Applications (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [VRP] Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und Subtour-Eliminierung.
+
★★★ Toth, P. & Vigo, D. (Hrsg., 2014):Vehicle Routing: Problems, Methods, and Applications (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [VRP] Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und Subtour-Eliminierung.
★★ Vidal, T., Laporte, G. & Matl, P. (2020):A concise guide to existing and emerging vehicle routing problem variants. In: European Journal of Operational Research 286(2), S. 401–416. DOI: 10.1016/j.ejor.2019.10.010 [VRP] Übersicht über die Variantenvielfalt — hilfreich, um die eigene Aufgabe einzuordnen, bevor man modelliert. Frei zugänglich als arXiv:1906.06750.
-
★ Kuhn, H. W. (1955):The Hungarian method for the assignment problem. In: Naval Research Logistics Quarterly 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [Zuordnungsproblem] Das Originalverfahren hinter scipy.optimize.linear_sum_assignment.
+
★ Kuhn, H. W. (1955):The Hungarian method for the assignment problem. In: Naval Research Logistics Quarterly 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [Zuordnungsproblem] Das Originalverfahren hinter scipy.optimize.linear_sum_assignment.
★★★ Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):Handbook of Metaheuristics (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 MetaheuristikenSammelband zu Tabu-Suche, Simulated Annealing, LNS, genetischen Verfahren — jeweils von den Urhebern geschrieben.
+
★★★ Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):Handbook of Metaheuristics (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 MetaheuristikenSammelband zu Tabu-Suche, Simulated Annealing, LNS, genetischen Verfahren — jeweils von den Urhebern geschrieben.
★★★ Pisinger, D. & Ropke, S. (2010):Large Neighborhood Search. In: Gendreau, M. & Potvin, J.-Y. (Hrsg.): Handbook of Metaheuristics, S. 399–419. Springer. DOI: 10.1007/978-1-4419-1665-5_13 MetaheuristikenDie Darstellung des Verfahrens, das im Buch die exakte Suche auf Teilproblemen mit heuristischem Zerstören verbindet.
★★ Kirkpatrick, S., Gelatt, C. D. & Vecchi, M. P. (1983):Optimization by Simulated Annealing. In: Science 220(4598), S. 671–680. DOI: 10.1126/science.220.4598.671 MetaheuristikenDie Originalarbeit; die Abkühlungsanalogie stammt von hier.
-
★★ Sörensen, K. (2015):Metaheuristics — the metaphor exposed. In: International Transactions in Operational Research 22(1), S. 3–18. DOI: 10.1111/itor.12001 MetaheuristikenKritik an der Flut naturanaloger „neuer” Verfahren. Vor der Auswahl einer Metaheuristik zu lesen — sie erspart viel Zeit.
+
★★ Sörensen, K. (2015):Metaheuristics — the metaphor exposed. In: International Transactions in Operational Research 22(1), S. 3–18. DOI: 10.1111/itor.12001 MetaheuristikenKritik an der Flut naturanaloger „neuer” Verfahren. Vor der Auswahl einer Metaheuristik zu lesen — sie erspart viel Zeit.
★★★ Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):Column Generation. Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.
+
★★★ Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):Column Generation. Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.
★★★ Lübbecke, M. E. & Desrosiers, J. (2005):Selected Topics in Column Generation. In: Operations Research 53(6), S. 1007–1023. DOI: 10.1287/opre.1050.0234 [Spaltengenerierung] Kompakter als das Buch und für den Einstieg besser geeignet; erklärt das Preisproblem sauber.
★★ Rahmaniani, R., Crainic, T. G., Gendreau, M. & Rei, W. (2017):The Benders decomposition algorithm: A literature review. In: European Journal of Operational Research 259(3), S. 801–817. DOI: 10.1016/j.ejor.2016.12.005 [Dekomposition] Übersicht über Varianten und Beschleunigungen — die Gegenrichtung zur Spaltengenerierung.
★★★ Boyd, S. & Vandenberghe, L. (2004):Convex Optimization. Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] Referenzwerk für konvexe Mengen, KKT-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter stanford.edu/~boyd/cvxbook.
-
★★ Kraft, D. (1988):A Software Package for Sequential Quadratic Programming. DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] Die Originalarbeit zu SLSQP — dem Verfahren hinter scipy.optimize.minimize.
-
★★ Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):Applications of second-order cone programming. In: Linear Algebra and its Applications 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [Konische Optimierung] Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in Kapitel 12.
+
★★★ Boyd, S. & Vandenberghe, L. (2004):Convex Optimization. Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] Referenzwerk für konvexe Mengen, KKT-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter stanford.edu/~boyd/cvxbook.
+
★★ Kraft, D. (1988):A Software Package for Sequential Quadratic Programming. DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] Die Originalarbeit zu SLSQP — dem Verfahren hinter scipy.optimize.minimize.
+
★★ Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):Applications of second-order cone programming. In: Linear Algebra and its Applications 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [Konische Optimierung] Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in Kapitel 12.
Optimierung unter Unsicherheit: stochastisch, robust, Chance Constraints
Zu Kapitel 12 und Kapitel 17. Aus dem früheren Block „Konvexe, nichtlineare und robuste Optimierung” herausgelöst und erweitert.
-
★★★ Birge, J. R. & Louveaux, F. (2011):Introduction to Stochastic Programming (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information (EVPI).
-
★★★ Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):Robust Optimization. Princeton University Press. DOI: 10.1515/9781400831050 [Robuste Optimierung] Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.
-
★★★ Bertsimas, D. & Sim, M. (2004):The Price of Robustness. In: Operations Research 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] Quelle des Budgeted-Uncertainty-Ansatzes: Der Parameter \Gamma regelt stufenlos, wie vorsichtig die Lösung wird.
+
★★★ Birge, J. R. & Louveaux, F. (2011):Introduction to Stochastic Programming (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information (EVPI).
+
★★★ Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):Robust Optimization. Princeton University Press. DOI: 10.1515/9781400831050 [Robuste Optimierung] Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.
+
★★★ Bertsimas, D. & Sim, M. (2004):The Price of Robustness. In: Operations Research 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] Quelle des Budgeted-Uncertainty-Ansatzes: Der Parameter\Gamma regelt stufenlos, wie vorsichtig die Lösung wird.
★★ Nemirovski, A. & Shapiro, A. (2007):Convex Approximations of Chance Constrained Programs. In: SIAM Journal on Optimization 17(4), S. 969–996. DOI: 10.1137/050622328 [Chance Constraints] Zeigt, wann sich eine Wahrscheinlichkeitsbedingung konvex annähern lässt — und wann nicht.
★★ Charnes, A. & Cooper, W. W. (1959):Chance-Constrained Programming. In: Management Science 6(1), S. 73–79. DOI: 10.1287/mnsc.6.1.73 [Chance Constraints] Die Originalarbeit, die den Begriff geprägt hat.
★★ Savage, S. L. (2009):The Flaw of Averages. Wiley, Hoboken. [Unsicherheit] Die populäre Darstellung des Fluchs des Durchschnitts. Ohne Formeln, dafür mit den Beispielen, die in Besprechungen wirken.
@@ -194,17 +194,17 @@
Dynamische Programmierung und sequenzielle Entscheidungen
★★★ Powell, W. B. (2022):Reinforcement Learning and Stochastic Optimization: A Unified Framework for Sequential Decisions. Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die Kapitel 13 am Schluss vornimmt.
-
★★★ Bertsekas, D. P. (2017):Dynamic Programming and Optimal Control, Vol. I (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] Die mathematisch strenge Darstellung des Optimalitätsprinzips und der Rückwärtsinduktion.
+
★★★ Powell, W. B. (2022):Reinforcement Learning and Stochastic Optimization: A Unified Framework for Sequential Decisions. Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die Kapitel 13 am Schluss vornimmt.
+
★★★ Bertsekas, D. P. (2017):Dynamic Programming and Optimal Control, Vol. I (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] Die mathematisch strenge Darstellung des Optimalitätsprinzips und der Rückwärtsinduktion.
★★ Sutton, R. S. & Barto, A. G. (2018):Reinforcement Learning: An Introduction (2. Aufl.). MIT Press, Cambridge. [Reinforcement Learning] Der Standardtext zum lernenden Gegenstück. Frei verfügbar auf der Seite der Autoren.
★ Bellman, R. (1957):Dynamic Programming. Princeton University Press. [Historisch] Das Originalwerk. Für die praktische Arbeit heute durch Powell und Bertsekas ersetzt.
★★★ Ehrgott, M. (2005):Multicriteria Optimization (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 Mehrkriterielle OptimierungPareto-Optimalität, Skalarisierung und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.
+
★★★ Ehrgott, M. (2005):Multicriteria Optimization (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 Mehrkriterielle OptimierungPareto-Optimalität, Skalarisierung und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.
★★ Miettinen, K. (1999):Nonlinear Multiobjective Optimization. Kluwer, Boston. DOI: 10.1007/978-1-4615-5563-6 Mehrkriterielle OptimierungSystematik der Verfahren, geordnet danach, wann die Präferenz des Entscheiders einfließt.
-
★★ Blank, J. & Deb, K. (2020):pymoo: Multi-Objective Optimization in Python. In: IEEE Access 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. pymoo.org
+
★★ Blank, J. & Deb, K. (2020):pymoo: Multi-Objective Optimization in Python. In: IEEE Access 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. pymoo.org
Lernbasierte Optimierung: maschinelles Lernen und OR
Zu Kapitel 15. Neuer Abschnitt — im Buch als eigenes Kapitel behandelt, in der Literaturliste bisher gar nicht vertreten.
★★★ Markowitz, H. (1952):Portfolio Selection. In: The Journal of Finance 7(1), S. 77–91. DOI: 10.2307/2975974 [Portfoliotheorie] Die Arbeit, die das Zusammenspiel von Rendite und Kovarianz zum Optimierungsproblem gemacht hat.
-
★★★ Rockafellar, R. T. & Uryasev, S. (2000):Optimization of Conditional Value-at-Risk. In: Journal of Risk 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] Der Satz, der die CVaR-Minimierung in ein lineares Programm überführt.
+
★★★ Rockafellar, R. T. & Uryasev, S. (2000):Optimization of Conditional Value-at-Risk. In: Journal of Risk 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] Der Satz, der die CVaR-Minimierung in ein lineares Programm überführt.
★★★ Ledoit, O. & Wolf, M. (2004):A well-conditioned estimator for large-dimensional covariance matrices. In: Journal of Multivariate Analysis 88(2), S. 365–411. DOI: 10.1016/S0047-259X(03)00096-4 [Kovarianzschätzung] Quelle des von scikit-learn verwendeten Shrinkage-Ziels (skalierte Einheitsmatrix).
★★★ Cornuéjols, G., Peña, J. & Tütüncü, R. (2018):Optimization Methods in Finance (2. Aufl.). Cambridge University Press. DOI: 10.1017/9781107297340 [Finanzoptimierung] Die Brücke zwischen Optimierungstheorie und Finanzanwendung; deckt QP, SOCP und robuste Portfolios ab.
★★★ Almgren, R. & Chriss, N. (2001):Optimal Execution of Portfolio Transactions. In: Journal of Risk 3(2), S. 5–39. DOI: 10.21314/jor.2001.041 [Orderausführung] Das Modell hinter dem Beispiel zur optimalen Orderausführung in Kapitel 13.
★★ Ledoit, O. & Wolf, M. (2003):Improved estimation of the covariance matrix of stock returns with an application to portfolio selection. In: Journal of Empirical Finance 10(5), S. 603–621. DOI: 10.1016/S0927-5398(03)00007-0 [Kovarianzschätzung] Die frühere Arbeit mit dem Konstant-Korrelations-Ziel.
★★ DeMiguel, V., Garlappi, L. & Uppal, R. (2009):Optimal Versus Naive Diversification: How Inefficient is the 1/N Portfolio Strategy? In: Review of Financial Studies 22(5), S. 1915–1953. DOI: 10.1093/rfs/hhm075 [Portfoliotheorie] Die Studie, die 1/N als ernstzunehmenden Vergleichsmaßstab etabliert hat — der Grund, warum im Buch jeder Optimierer gegen sie antreten muss.
-
★★ López de Prado, M. (2018):Advances in Financial Machine Learning. Wiley, Hoboken. [Backtesting] Backtest-Overfitting, Deflated Sharpe Ratio, Denoising der Kovarianzmatrix. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.
+
★★ López de Prado, M. (2018):Advances in Financial Machine Learning. Wiley, Hoboken. [Backtesting] Backtest-Overfitting, Deflated Sharpe Ratio, Denoising der Kovarianzmatrix. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.
★★ Bailey, D. H. & López de Prado, M. (2014):The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality. In: The Journal of Portfolio Management 40(5), S. 94–107. DOI: 10.3905/jpm.2014.40.5.094 [Backtesting] Beziffert, wie stark eine Sharpe Ratio zu korrigieren ist, wenn man viele Strategien ausprobiert hat.
Praxis: Betrieb, Test und Auslieferung von Optimierungsmodellen
★★★ Chinneck, J. W. (2008):Feasibility and Infeasibility in Optimization: Algorithms and Computational Methods. Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen (IIS). Direkte Grundlage von Anhang C.
+
★★★ Chinneck, J. W. (2008):Feasibility and Infeasibility in Optimization: Algorithms and Computational Methods. Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen (IIS). Direkte Grundlage von Anhang C.
★★ Sculley, D., Holt, G., Golovin, D. et al. (2015):Hidden Technical Debt in Machine Learning Systems. In: Advances in Neural Information Processing Systems 28, S. 2503–2511. [Betrieb] Über Systeme, deren Modell nur ein kleiner Teil ist — überträgt sich unmittelbar auf den Betrieb von Optimierungsdiensten. Frei zugänglich.
-
★★ Kleppmann, M. (2017):Designing Data-Intensive Applications. O’Reilly, Sebastopol. [Betrieb] Snapshot-Prinzip, Zustandslosigkeit, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in Kapitel 22.
+
★★ Kleppmann, M. (2017):Designing Data-Intensive Applications. O’Reilly, Sebastopol. [Betrieb] Snapshot-Prinzip, Zustandslosigkeit, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in Kapitel 22.
★★ Ramírez, S.:FastAPI Documentation.fastapi.tiangolo.com [Betrieb] Die Grundlage des Optimierungsdienstes in Kapitel 23, einschließlich der Eingabevalidierung über pydantic.
INFORMS — Institute for Operations Research and the Management Sciences. www.informs.org
GOR — Gesellschaft für Operations Research e. V., deutschsprachiger Fachverband. www.gor-ev.de
EURO — Association of European Operational Research Societies. www.euro-online.org
-
Basel Committee on Banking Supervision:Minimum capital requirements for market risk (Basel III, FRTB). Bank for International Settlements. Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf Expected Shortfall.
+
Basel Committee on Banking Supervision:Minimum capital requirements for market risk (Basel III, FRTB). Bank for International Settlements. Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf Expected Shortfall.
Europäische Union:MiFID II (Richtlinie 2014/65/EU) sowie die ESMA-Leitlinien zum algorithmischen Handel. Regulatorischer Rahmen für die Handelsmaschine aus Kapitel 21.
Änderungsprotokoll
@@ -269,7 +269,7 @@
Entfernt: kein einziger Eintrag. Alle 27 Einträge der Vorgängerfassung wurden geprüft; sie sind korrekt zitiert, in ihrem Feld weiterhin maßgeblich und nicht redundant zueinander. Es gab damit keine Streichkandidaten. Zwei Einträge sind jedoch auf ★ optional herabgestuft, weil sie historisch bedeutend, für die tägliche Arbeit aber entbehrlich sind: Dantzig (1963) und Bellman (1957) — beide inzwischen durch modernere Darstellungen ersetzt, die im selben Block stehen. Hillier & Lieberman (2021) bleibt bei ★★, ist aber ausdrücklich als Überblickswerk ohne Python-Bezug gekennzeichnet.
Neu aufgenommen: 60 Einträge — von 27 auf 87. Davon sind 72 mit einer Relevanzangabe versehen (34 × ★★★, 35 × ★★, 3 × ★); die übrigen 15 sind Softwareverweise, Datenquellen und Institutionen ohne Autorenschaft. 63 Einträge tragen eine Jahresangabe, 22 davon stammen aus 2018 oder später.
Zur Vorgabe „bevorzugt Werke ab 2018”: Sie ließ sich nur teilweise einlösen, und zwar aus der Sache heraus. In weiten Teilen des Operations Research sind die maßgeblichen Werke älter — Ahuja/Magnanti/Orlin (1993) zu Netzwerkflüssen, Boyd/Vandenberghe (2004) zur konvexen Optimierung, Ehrgott (2005) zur Mehrkriterialität, Birge/Louveaux (2011) zur stochastischen Programmierung. Sie durch jüngere, aber weniger maßgebliche Titel zu ersetzen hätte die Liste verschlechtert. Neuer als 2018 ist konsequent dort, wo sich das Feld tatsächlich bewegt hat: Solver-Benchmarks, Python-Bibliotheken und lernbasierte Optimierung.
-
Neue Themenabschnitte: zwölf. Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: Modellierung in Python, Lineare und ganzzahlige Optimierung, Moderne Solver und Benchmarks, Graphen, Flüsse und Tourenplanung, Metaheuristiken, Spaltengenerierung und Dekomposition, Optimierung unter Unsicherheit, Dynamische Programmierung, Mehrkriterielle Optimierung, Lernbasierte Optimierung, Supply Chain, Energie und Produktionsplanung sowie Praxis: Betrieb, Test und Auslieferung. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung” wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
+
Neue Themenabschnitte: zwölf. Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: Modellierung in Python, Lineare und ganzzahlige Optimierung, Moderne Solver und Benchmarks, Graphen, Flüsse und Tourenplanung, Metaheuristiken, Spaltengenerierung und Dekomposition, Optimierung unter Unsicherheit, Dynamische Programmierung, Mehrkriterielle Optimierung, Lernbasierte Optimierung, Supply Chain, Energie und Produktionsplanung sowie Praxis: Betrieb, Test und Auslieferung. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung” wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
Nicht angelegt wurden zwei erwogene Abschnitte:Diskrete Ereignissimulation und Cloud-basierte Solver-Nutzung. Beide Themen kommen im Buch nicht vor — eine Literaturliste soll das Buch erschließen und nicht darüber hinausweisen. Der API-gestützte Betrieb eines Solvers ist stattdessen im Praxisblock erfasst.
Prüfung gegen Crossref
Alle Einträge wurden einzeln gegen die Crossref-API geprüft — bei einem Literaturverzeichnis ist eine erfundene oder falsch zitierte Quelle der schwerste Fehler, weil der Leser sie sucht und nicht findet.
A.1 Lösungen zu Kapitel „Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung“
1.1 — Analytik-Stufen. (a) deskriptiv (Vergangenheit beschreiben). (b) prädiktiv (Prognose). (c) präskriptiv (Handlungsanweisung unter Restriktionen). (d) prädiktiv (Klassifikation). (e) präskriptiv — hier kommt das begrenzte Budget ins Spiel, es muss zugeteilt werden. Beachten Sie das Paar (d)/(e): Die Prognose sagt, wer kündigen wird; die Optimierung sagt, wen man mit dem vorhandenen Geld halten kann.
-
1.2 — Hart oder weich? (a) hart — gesetzlich zwingend. (b) weich — Wunsch, mit Strafkosten. (c) hart — Patientensicherheit, rechtlich vorgeschrieben. (d) weich — Fairnessziel, über Strafterme. (e) hart, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
+
1.2 — Hart oder weich? (a) hart — gesetzlich zwingend. (b) weich — Wunsch, mit Strafkosten. (c) hart — Patientensicherheit, rechtlich vorgeschrieben. (d) weich — Fairnessziel, über Strafterme. (e) hart, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
1.3 — Kombinatorik. (a) 12! = 479\,001\,600. (b) 479\,001\,600 / 5\cdot10^6 \approx 95{,}8 Sekunden \approx 1{,}6 Minuten. (c) 13! = 6\,227\,020\,800; das sind 1245 s \approx 20{,}8 Minuten — Faktor 13. Jeder weitere Auftrag multipliziert die Zeit mit der neuen Anzahl. (d) Eine Stunde = 3600 s \to 1{,}8\cdot10^{10} Prüfungen. 13! = 6{,}2\cdot10^9 ✓, 14! = 8{,}7\cdot10^{10} ✗. Also 13 Aufträge.
-
1.4 — Modell lesen. (a) Variablen x_1, x_2 \ge 0; Parameter (3,5) und die Kapazitäten (4,12,18); Zielfunktion \max 3x_1+5x_2; vier Nebenbedingungen inkl. Nichtnegativität. (b) (2,6): 2\le4 ✓, 12\le12 ✓, 6+12=18\le18 ✓ → zulässig, Z=36. (4,3): 4\le4 ✓, 6\le12 ✓, 12+6=18\le18 ✓ → zulässig, Z=27. (c) Beste ganzzahlige Lösung ist (2,6) mit Z=36 — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
+
1.4 — Modell lesen. (a) Variablen x_1, x_2 \ge 0; Parameter(3,5) und die Kapazitäten (4,12,18); Zielfunktion\max 3x_1+5x_2; vier Nebenbedingungen inkl. Nichtnegativität. (b) (2,6): 2\le4 ✓, 12\le12 ✓, 6+12=18\le18 ✓ → zulässig, Z=36. (4,3): 4\le4 ✓, 6\le12 ✓, 12+6=18\le18 ✓ → zulässig, Z=27. (c) Beste ganzzahlige Lösung ist (2,6) mit Z=36 — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
1.5 — Bäckerei.
from ortools.sat.python import cp_modelm = cp_model.CpModel()
@@ -121,8 +121,8 @@
s = cp_model.CpSolver(); s.Solve(m)
Zwei lehrreiche Beobachtungen: 1. Die Vertragsbedingung x_1 \ge 40 ist nicht bindend — der Solver backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch. 2. Das kontinuierliche Optimum (40;\ 116{,}67) liefert denselben Zielwert 450. Das Problem hat also mehrere optimale Lösungen — die Zielfunktion verläuft parallel zur Mehlrestriktion (2{,}5/0{,}5 = 5 = 3{,}0/0{,}6). Prüfen Sie das nach: Genau deshalb ist hier auch die ganzzahlige Lösung ohne Verlust erreichbar.
-
1.6 — Sensitivität durch Ausprobieren. Schleife über RAM_GESAMT in range(54, 73, 2), jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die vCPU-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der Schattenpreis (Kapitel 5). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
+
Zwei lehrreiche Beobachtungen: 1. Die Vertragsbedingung x_1 \ge 40 ist nicht bindend — der Solver backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch. 2. Das kontinuierliche Optimum (40;\ 116{,}67) liefert denselben Zielwert 450. Das Problem hat also mehrere optimale Lösungen — die Zielfunktion verläuft parallel zur Mehlrestriktion (2{,}5/0{,}5 = 5 = 3{,}0/0{,}6). Prüfen Sie das nach: Genau deshalb ist hier auch die ganzzahlige Lösung ohne Verlust erreichbar.
+
1.6 — Sensitivität durch Ausprobieren. Schleife über RAM_GESAMT in range(54, 73, 2), jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die vCPU-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der Schattenpreis (Kapitel 5). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
1.7 — Eigenes Problem. Individuell. Prüfkriterien: Sind die Variablen wirklich entscheidbar (nicht bereits festgelegt)? Hat die Zielfunktion eine Einheit? Ist jede harte Bedingung wirklich unverhandelbar?
Finde den Denkfehler — Die Schreinerei verdoppelt ihren Gewinn
@@ -159,7 +159,7 @@
2.1 — Matrixform lesen.\max 4x_1 + x_2 + 6x_3 u. d. N. x_1 + 2x_2 \le 10, x_2 + 3x_3 \le 12, x \ge 0. 3 Variablen, 2 Nebenbedingungen (plus Nichtnegativität).
2.2 — Standardform.\min -7x_1 + 2x_2 u. d. N. -4x_1 - x_2 \le -20 (aus „\ge“ durch Multiplikation mit -1), x_1 - x_2 \le 3und-x_1 + x_2 \le -3 (Gleichung als zwei Ungleichungen), x_1, x_2 \ge 0.
2.3 — Ecken von Hand. (b) Ecken: (0,0), (6,0), (4,4), (0,8). (c) Z: 0, 12, 20, 24 → Optimum (0,8) mit Z = 24. (d) Bei \max 2x_1+2x_2: Z(4,4) = 16, Z(0,8) = 16 — die Zielfunktion ist parallel zur Kantex_1+x_2=8. Es gibt dann unendlich viele optimale Lösungen (die ganze Kante), aber weiterhin mindestens eine in einer Ecke — der Fundamentalsatz bleibt gültig.
-
2.4 — Konvexität. (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex (f'' = 12x^2 \ge 0). (c) nicht konvex, sondern konkav (f'' = -\tfrac14 x^{-3/2} < 0). (d) nicht konvex, konkav (f'' = -1/x^2 < 0). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix 2\mathbf{I} \succ 0). (f) konvexe Menge (Kreisscheibe). (g) konvexe Menge — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die Funktion1/x ist konvex, und die Menge \{x_2 \ge 1/x_1\} ist der Epigraph einer konvexen Funktion, also konvex).
+
2.4 — Konvexität. (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex (f'' = 12x^2 \ge 0). (c) nicht konvex, sondern konkav (f'' = -\tfrac14 x^{-3/2} < 0). (d) nicht konvex, konkav (f'' = -1/x^2 < 0). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix 2\mathbf{I} \succ 0). (f) konvexe Menge (Kreisscheibe). (g) konvexe Menge — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die Funktion1/x ist konvex, und die Menge \{x_2 \ge 1/x_1\} ist der Epigraph einer konvexen Funktion, also konvex).
2.6 — Bäckerei visualisieren. Ecken: (40,0), (150,0), (40,116{,}67) und der Schnittpunkt von Mehl- und Ofengrenze. Beste Ecke ist (40;\ 116{,}67) mit Z = 450 (im kontinuierlichen Fall).
2.7 — Eigenwert-Clipping.
@@ -169,7 +169,7 @@
Für \mathbf{P}_2 ergibt sich eine PSD-Matrix mit Korrelation exakt 1{,}0 — das Verfahren zieht die unmögliche Korrelation auf den nächstgelegenen zulässigen Wert. Für Kovarianzmatrizen setzt man in der Praxis auf einen kleinen positiven Wert statt auf 0 (np.maximum(lam, 1e-10)), damit die Matrix invertierbar bleibt.
Finde den Denkfehler — Die unauffällige Transposition
-
Warum nichts auffällt.\mathbf{A} ist quadratisch, also passen die Dimensionen auch transponiert. NumPy prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges LP vorgelegt und löst es korrekt — nur eben ein anderes. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos aussieht: Wäre die Matrix 2 \times 3 gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
+
Warum nichts auffällt.\mathbf{A} ist quadratisch, also passen die Dimensionen auch transponiert. NumPy prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges LP vorgelegt und löst es korrekt — nur eben ein anderes. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos aussieht: Wäre die Matrix 2 \times 3 gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
Der Plan an den echten Restriktionen. Mit \mathbf{A} = \begin{pmatrix} 1 & 2 \\ 3 & 1\end{pmatrix} und \mathbf{x} = (5{,}6;\ 0{,}8):
Vorbeugend hilft außerdem, Matrizen nie positionsweise abzutippen, sondern über benannte Spalten aus einer Tabelle zu erzeugen — genau das tut Excel_Bruecke.py aus Kapitel 1.
Micro-Quiz
1 — (b). Der Fundamentalsatz sagt nur, dass ein Optimum in einer Ecke angenommen wird. (a) ist falsch, weil es mehrere optimale Ecken geben kann (wenn die Zielfunktion parallel zu einer Kante verläuft) und dann sogar unendlich viele optimale Punkte auf der Verbindungsstrecke. (c) ist falsch, weil die Zahl der Eckenkandidaten kombinatorisch wächst: Bei 6 Variablen und 4 Ungleichungen plus 6 Nichtnegativitäten sind \binom{10}{6} = 210 Systeme zu prüfen — bei 50 Variablen wären es 10^{29}. Der Satz sagt, wo man suchen muss, nicht dass die Suche billig ist.
-
2 — (b). Faustregel \kappa = 10^{k} ⟹ etwa k signifikante Stellen verloren; bei 10^{11} bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die Konditionszahl mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
+
2 — (b). Faustregel \kappa = 10^{k} ⟹ etwa k signifikante Stellen verloren; bei 10^{11} bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die Konditionszahl mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
3 — (c).int() schneidet ab: int(0.99999998) == 0 macht aus einer Ja- eine Nein-Entscheidung (a). Den Wert unverändert weiterzureichen (b) verschiebt das Problem nur in die nachgelagerte Verarbeitung, wo dann irgendwann doch jemand int() schreibt. Richtig ist die Prüfung gegen die Toleranz mit Fehlermeldung im Zweifelsfall — die Funktion sichere_ganzzahl() aus Skalierung_Kondition.py.
Selbsttest
@@ -196,18 +196,18 @@
A.3 Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“
-
3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) MILP über highspy oder CP-SAT (Standortproblem mit Fixkosten). (f) scipy.optimize.minimize mit Multistart (nicht konvex).
+
3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) MILP über highspy oder CP-SAT (Standortproblem mit Fixkosten). (f) scipy.optimize.minimize mit Multistart (nicht konvex).
3.2 — Bäckerei viermal. Alle vier müssen Z = 448 (ganzzahlig) bzw. 450 (kontinuierlich) liefern. Denken Sie an die Prozesstrennung, falls ortools und highspy kollidieren.
-
3.3 — CSR-Format.values = [3, 1, 2, 5, 4, 6], indices = [0, 3, 2, 0, 1, 3], starts = [0, 2, 3]. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte 3\times4 = 12. Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
+
3.3 — CSR-Format.values = [3, 1, 2, 5, 4, 6], indices = [0, 3, 2, 0, 1, 3], starts = [0, 2, 3]. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte 3\times4 = 12. Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
3.4 — Konvexitätsprüfung.\min x^3 wirft DCPError: Problem does not follow DCP rules, weil x^3 auf [-2,2] weder konvex noch konkav ist. \min x^2 läuft und liefert x=0. Der Unterschied: CVXPY akzeptiert nur Ausdrücke, deren Konvexität es beweisen kann — dafür garantiert es das globale Optimum.
3.5 — Laufzeitvergleich. Erwartetes Muster: linprog und highspy liegen bei kleinen Modellen gleichauf (Overhead dominiert); ab etwa n \gtrsim 500 zieht highspy davon, weil der Modellaufbau effizienter ist. CVXPY hat den größten festen Aufwand (Ausdrucksbaum-Kompilierung), der bei wiederholten Läufen mit cp.Parameter teilweise entfällt.
-
3.6 — Eigene Entscheidungshilfe. Ergänzungen: Bei kommerzieller Lizenz Gurobi/CPLEX über Pyomo oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo (algebraische Notation, Trennung von Daten und Modell).
+
3.6 — Eigene Entscheidungshilfe. Ergänzungen: Bei kommerzieller Lizenz Gurobi/CPLEX über Pyomo oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo (algebraische Notation, Trennung von Daten und Modell).
Finde den Denkfehler — Der Solver, der angeblich dreimal schneller ist
Was da alles mitgemessen wird. Die Stoppuhr läuft ab der ersten Zeile, also mindestens über drei Dinge, die mit Lösegeschwindigkeit nichts zu tun haben:
-
Der import. CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. linprog steht in einem Prozess, der SciPy ohnehin schon geladen hat, praktisch sofort bereit.
+
Der import. CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. linprog steht in einem Prozess, der SciPy ohnehin schon geladen hat, praktisch sofort bereit.
Der Modellaufbau. CVXPY baut einen Ausdrucksbaum und kompiliert ihn in die Standardform des Solvers. Das ist echter Aufwand — aber Aufbau, nicht Rechnen.
Die Reihenfolge. CVXPY läuft zuerst und bezahlt dabei alles, was danach im Betriebssystem-Cache liegt: Bibliotheken, Speicherseiten, JIT-Wärme. Tauschen Sie die beiden Blöcke, und die Zahlen verschieben sich allein deshalb.
@@ -237,14 +237,14 @@
Micro-Quiz
1 — (b) CP-SAT. Es geht um diskrete Zuweisung mit logischen Regeln („nicht gleichzeitig“) — CP-SATs Kerngebiet, und Regeln dieser Art formuliert man dort direkt statt über Big-M-Umwege. (a) scheidet aus, weil CVXPY keine sinnvolle Ganzzahligkeit in dieser Größenordnung bietet; (c) findet bei einem diskreten Problem bestenfalls ein lokales Optimum und hat keinerlei Handhabe für „entweder–oder“-Regeln.
2 — (c) Modellaufbau vektorisieren. 32 von 40 Sekunden fallen an, bevor der Solver startet. Ein kommerzieller Solver (a) beschleunigt bestenfalls die verbleibenden 8 Sekunden — selbst bei Faktor 4 gewinnen Sie 6 der 40 Sekunden. Das Zeitlimit (b) betrifft den Abbruch, nicht die Geschwindigkeit. Die Regel dahinter: erst messen, wo die Zeit hingeht, dann optimieren.
-
3 — (b). Alle vier sind Modellierungsschichten über HiGHS. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver (Kapitel 11).
+
3 — (b). Alle vier sind Modellierungsschichten über HiGHS. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver (Kapitel 11).
Selbsttest
Modellierungsschicht (Python) und Solver-Schicht (C++). Die Trennung erlaubt Solvertausch ohne Modelländerung.
CVXPY prüft die Konvexität und lehnt ab, was es nicht garantieren kann; minimize prüft nichts und liefert ein lokales Optimum.
Werte, Spaltenindizes und Zeilenstartpositionen der Nicht-Null-Einträge. Entscheidend, weil reale Modelle extrem dünn besetzt sind.
Wegen des Kompilierungsaufwands des Ausdrucksbaums. Kein Argument dagegen, weil dieser Aufwand einmalig ist, die Lesbarkeit hoch und die Konvexitätsprüfung wertvoll.
-
Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die Routing-Bibliothek überlegen).
+
Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die Routing-Bibliothek überlegen).
A.4 Lösungen zu Kapitel „Vom Management-Wunsch zum Modell“
@@ -327,7 +327,7 @@
5.1 — Schlupf deuten. (a) Ressourcen 1 und 3 (Schlupf 0). (b) Ja: Überall ist s_i \cdot y_i = 0. (c) In Ressource 3 — der höchste Schattenpreis (9,8) bedeutet den größten Grenznutzen.
5.2 — Vorzeichen. Er hat die Zielfunktion für linprog negiert und die Dualwerte nicht zurückgedreht. Der korrekte Schattenpreis ist +45 €.
5.3 — Simplex von Hand. Starttableau mit s_1, s_2 in der Basis. Erste Iteration: Pivotspalte x_1 (-5), Quotienten 24/6 = 4 und 6/1 = 6 → Pivotzeile 1. Nach dem Tausch: x_1 = 4, Z = 20. Zweite Iteration: Pivotspalte x_2, Pivotzeile 2 → x_1 = 3, x_2 = 1{,}5, Z = 21. Optimum:\mathbf{x}^* = (3;\ 1{,}5), Z^* = 21, Schattenpreise y^* = (0{,}75;\ 0{,}50).
5.5 — Unbeschränktheit. Der Solver wirft „Problem ist unbeschränkt“. Geometrisch: Der zulässige Bereich \{x_1 - x_2 \le 5,\ x \ge 0\} ist nach oben offen — man kann x_1 und x_2 gemeinsam beliebig wachsen lassen (z. B. x_1 = x_2 = t für t \to \infty), ohne eine Bedingung zu verletzen, und Z = 2t wächst mit. In der Praxis fehlt fast immer eine Kapazitätsgrenze.
5.6 — Gültigkeitsbereich. (a) Bei ca. 66,7 Stunden Prüfkapazität wird die Lackierzeit zum Engpass; der Schattenpreis der Prüfung fällt dann. (b) Zukaufen lohnt bis zu dem Punkt, an dem der Schattenpreis unter 18 €/h fällt. (c) Der Gewinnverlauf ist stückweise linear und konkav: Jedes Teilstück hat die Steigung des jeweils gültigen Schattenpreises, und die Steigungen werden immer flacher — jede zusätzliche Einheit bringt weniger, weil andere Engpässe nachrücken.
5.7 — Phase 1. Ansatz: Für jede Zeile mit b_i < 0 (nach Umformung zu \ge) eine künstliche Variable a_i \ge 0 einführen, Hilfszielfunktion \min \sum a_i lösen. Ist das Minimum 0, existiert eine zulässige Basislösung, und man startet Phase 2 mit dem erreichten Tableau. Ist es > 0, ist das Problem unzulässig. Für das Beispiel: Optimum (12;\ 0), Z = 36.
@@ -409,7 +409,7 @@
Das ist die allgemeine Regel für jede Sensitivitätsaussage über mehr als eine Grenzeinheit: Der Schattenpreis zeigt die Richtung, die Neuberechnung liefert den Betrag. Bei einer Investitionsentscheidung kostet ein zusätzlicher Solverlauf Millisekunden — und hier hätte er 380 000 € gespart.
Micro-Quiz
1 — (b). Genau der Satz vom komplementären Schlupf: s_i \cdot y_i = 0. Ist Schlupf vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil verkehrt; (c) verwechselt Schlupf mit Auslastung — s_i = 12 heißt gerade, dass 12 Einheiten übrig sind.
-
2 — (b) Entartung. 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
+
2 — (b) Entartung. 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
3 — (b). Der Schlupf ist Solver-Rauschen in der Größenordnung der Maschinengenauigkeit — rechnerisch null, aber nicht == 0.0. Richtig ist abs(schlupf) < 1e-7. (a) unterstellt dem Solver einen Fehler, den er nicht gemacht hat; (c) ist frei erfunden — Schlupfwerte sind bei korrekt aufgestelltem Modell nie negativ, abgesehen von genau diesem Rauschen.
Selbsttest
@@ -422,9 +422,9 @@
A.6 Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“
6.1 — Runden widerlegen. Beispiel: \max x_1 + x_2 u. d. N. 10x_1 + 10x_2 \le 15, ganzzahlig. LP-Optimum Z = 1{,}5; Abrunden ergibt (0,0) mit Z = 0 — 100 % Verlust. Das ganzzahlige Optimum ist (1,0) mit Z = 1. Prinzip: kleine Zahlen plus knappe Kapazität.
-
6.2 — Big-M wählen.M = 250 (die bekannte Kapazität). Bei M = 10^6 bleibt das Modell korrekt, aber die LP-Relaxation wird extrem schwach: y_j darf schon bei x_j/10^6 liegen, die Schranke ist praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
+
6.2 — Big-M wählen.M = 250 (die bekannte Kapazität). Bei M = 10^6 bleibt das Modell korrekt, aber die LP-Relaxation wird extrem schwach: y_j darf schon bei x_j/10^6 liegen, die Schranke ist praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
6.4 — Branch-and-Bound. LP-Relaxation der Wurzel: nach Nutzen/Gewicht sortieren (8/5=1{,}6; 11/7=1{,}57; 6/4=1{,}5; 4/3=1{,}33). Gierig füllen: x_1=1 (Rest 9), x_2=1 (Rest 2), x_3 = 0{,}5 → Z_{LP} = 8+11+3 = 22. Verzweigen über x_3. Ast x_3=0: x_1=1,x_2=1,x_4=2/3 → Z = 21{,}67; weiter verzweigen → beste ganzzahlige Lösung (1,1,0,0) mit Z=19. Ast x_3=1: x_1=1, x_3=1, Rest 5 → x_2=5/7 → Z = 21{,}86; verzweigen führt auf (1,0,1,1) mit Z = 18 und (0,1,1,0) mit Z=17. Optimum: (1,1,0,0), Z = 19.
+
6.4 — Branch-and-Bound. LP-Relaxation der Wurzel: nach Nutzen/Gewicht sortieren (8/5=1{,}6; 11/7=1{,}57; 6/4=1{,}5; 4/3=1{,}33). Gierig füllen: x_1=1 (Rest 9), x_2=1 (Rest 2), x_3 = 0{,}5 → Z_{LP} = 8+11+3 = 22. Verzweigen über x_3. Ast x_3=0: x_1=1,x_2=1,x_4=2/3 → Z = 21{,}67; weiter verzweigen → beste ganzzahlige Lösung (1,1,0,0) mit Z=19. Ast x_3=1: x_1=1, x_3=1, Rest 5 → x_2=5/7 → Z = 21{,}86; verzweigen führt auf (1,0,1,1) mit Z = 18 und (0,1,1,0) mit Z=17. Optimum: (1,1,0,0), Z = 19.
6.5 — Kardinalität variieren. (a) Ab K = 3 steigt der Ertrag nicht mehr wesentlich, weil bereits drei Positionen à 40 000 € das Budget von 100 000 € abdecken können. (b) Rechenzeit steigt zunächst (mehr Kombinationen), fällt bei großem K wieder (Restriktion bindet nicht mehr). (c) Der faire Preis ist die Differenz der Netto-Erträge zwischen K=3 und K=4 — bei den gegebenen Daten nahe null, weil die Obergrenze von 40 000 € bereits bindet.
6.6 — Big-M-Effekt. Erwartetes Muster: Knotenzahl und Laufzeit steigen deutlich mit M. Bei M = 10^9 ist die Relaxation so schwach, dass der Solver kaum noch prunen kann.
6.7 — Standortplanung. Modell wie in Projekt P4. Prüfen Sie: Gesamtkapazität der eröffneten Lager \ge Gesamtbedarf (140); bei Kapazität 80 je Lager sind mindestens \lceil 140/80 \rceil = 2 Lager nötig.
@@ -458,8 +458,8 @@
Vorbeugend gilt weiter die Regel aus dem Kapitel: M so klein wie möglich, hergeleitet aus einer echten Kapazität. Hier wäre das schlicht die Lagerkapazität — mehr kann ein Lager ohnehin nicht ausliefern.
Micro-Quiz
1 — (b). Der Gap (48\,200 - 47\,100)/48\,200 = 2{,}3\,\% ist eine Garantie, keine Schätzung: Besser als 47 100 € kann es nachweislich nicht werden, also ist der vorliegende Plan höchstens 2,3 % zu teuer. (a) wirft eine völlig brauchbare Lösung weg — genau der Fehler, den if status == OPTIMAL: ... else: return None produziert. (c) verwechselt die Schranke mit einem erreichbaren Wert: 47 100 € ist eine untere Schranke, es ist völlig offen, ob ein Plan mit diesen Kosten überhaupt existiert.
-
2 — (b). Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der Trickle Flow dagegen ist still: Das Modell meldet Optimal, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
-
3 — (c). Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und INFEASIBLE bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen Warm-Start-Effekt, den es gar nicht gibt.
+
2 — (b). Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der Trickle Flow dagegen ist still: Das Modell meldet Optimal, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
+
3 — (c). Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und INFEASIBLE bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen Warm-Start-Effekt, den es gar nicht gibt.
Selbsttest
Weil die Relaxation mehr Lösungen zulässt (alle ganzzahligen plus gebrochene) — das Maximum über einer größeren Menge ist mindestens so groß.
@@ -473,7 +473,7 @@
7.1 — Propagation. Aus x_1 + x_2 = 8 und x_1 < x_2 folgt x_1 < 4, also x_1 \in \{2,3\} (denn x_2 = 8-x_1 \le 6 verlangt x_1 \ge 2) und entsprechend x_2 \in \{5,6\}. Aus 36 Kombinationen werden 2 zulässige.
7.2 — Hart oder weich. (a) hart. (b) weich, mittlere Strafe (~50). (c) hart. (d) weich, mittlere Strafe (~80, weil geteilte Dienste stark belasten). (e) hart, falls gesetzlich; sonst weich mit sehr hoher Strafe (~1000).
7.3 — Regeln ergänzen. (a) for s in (2,3): modell.Add(x["Frau_Albrecht", s] == 0) (b) Hilfsvariablen arbeitet_bauer, arbeitet_koch per AddMaxEquality an die Zuweisungssummen koppeln, dann modell.Add(arbeitet_bauer + arbeitet_koch <= 1). (c) Belohnung = negative Strafe: strafterme.append(-30 * folge_var), wobei folge_var per AddBoolAnd/OnlyEnforceIf an x_{p,0} \wedge x_{p,1} gekoppelt wird.
-
7.4 — Infeasibility. Mit MAX_VERTRETUNGEN = 0 meldet der Solver INFEASIBLE. Nach Einbau der Schlupfvariablen lässt der Solver alle vier Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei MAX_VERTRETUNGEN = 1: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
+
7.4 — Infeasibility. Mit MAX_VERTRETUNGEN = 0 meldet der Solver INFEASIBLE. Nach Einbau der Schlupfvariablen lässt der Solver alle vier Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei MAX_VERTRETUNGEN = 1: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
7.5 — Sudoku.
x = [[m.NewIntVar(1, 9, f"x{i}{j}") for j inrange(9)] for i inrange(9)]for i inrange(9): m.AddAllDifferent(x[i]) # Zeilen
@@ -507,7 +507,7 @@
-
Nur die Kombination trägt. (c) Bei 15 Aufträgen (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet FEASIBLE mit Makespan 200; acht Arbeiter melden OPTIMAL mit demselben Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im Beweis, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
+
Nur die Kombination trägt. (c) Bei 15 Aufträgen (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet FEASIBLE mit Makespan 200; acht Arbeiter melden OPTIMAL mit demselben Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im Beweis, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
7.7 — Job-Shop erweitern. (a) Rüstzeiten: AddNoOverlap durch paarweise Disjunktionen mit Übergangszeit ersetzen, oder AddCircuit je Maschine mit Übergangsmatrix. (b) Verspätung: tardiness = MaxEquality(0, ende - faellig), in die Zielfunktion. (c) AddCumulative(intervalle, [1]*n, 2) statt AddNoOverlap für die Doppelmaschine.
7.8 — Wochendienstplan. Siehe Projekt P2. Kernpunkte: Nachtschicht-Folgeregel über AddImplication; „höchstens 5 Tage in Folge“ über gleitende Fenster (sum(x[p,t..t+5]) <= 5).
Finde den Denkfehler — Die Betriebsvereinbarung, die niemanden interessiert
@@ -579,7 +579,7 @@
Micro-Quiz
1 — (b) wirkungslos.INFEASIBLE ist kein Abbruch, sondern ein Beweis: CP-SAT hat gezeigt, dass keine zulässige Lösung existiert. Mehr Zeit (a) oder mehr Arbeiter (c) ändern daran nichts — sie bestätigen dasselbe Ergebnis nur schneller. Der Unterschied zu UNKNOWN ist genau dieser: Dort wurde nichts gefunden, hier gibt es nichts. Die Ursache liegt in den harten Regeln; Anhang C zeigt, wie man die widersprüchliche Teilmenge isoliert.
2 — (b).AddNoOverlap ist kompakter und propagiert stärker, weil der spezialisierte Propagator alle Intervalle gemeinsam betrachtet statt paarweise. (a) ist falsch: Beide Formulierungen beschreiben dieselbe Menge zulässiger Lösungen, also auch dasselbe Optimum — der Unterschied liegt in der Laufzeit, nicht in der Qualität. (c) ist falsch: CP-SAT rechnet ausschließlich mit ganzen Zahlen, kontinuierliche Zeiten kann es gerade nicht.
-
3 — (b). Mehrfache Optima sind bei Scheduling der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: assert plan == erwarteter_plan besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen assert makespan == 11 und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert num_workers und random_seed.
+
3 — (b). Mehrfache Optima sind bei Scheduling der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: assert plan == erwarteter_plan besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen assert makespan == 11 und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert num_workers und random_seed.
Selbsttest
Sie entfernt Werte aus den Wertebereichen, die aufgrund der Bedingungen unmöglich sind — bevor gesucht wird. Dadurch schrumpft der Suchbaum drastisch.
@@ -592,7 +592,7 @@
A.8 Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“
8.2 — Unlösbarkeit. Die Summe aller Flusserhaltungsgleichungen ergibt \sum_i b_i = 0 (jede Kante taucht einmal mit +1 und einmal mit -1 auf). Ist die Summe ungleich null, widersprechen sich die Gleichungen. Bei Angebotsüberschuss führt man einen künstlichen Dummy-Senkenknoten mit dem Restbedarf und Kosten 0 ein.
Beachten Sie, dass Werk 3 seine günstigste Verbindung (L4 zu 5) voll ausschöpft und W2 ausschließlich L1 beliefert (9), obwohl L4 mit 7 billiger wäre — dort ist der Bedarf bereits von W3 gedeckt. (c) Die Lösung ist ganzzahlig, obwohl nicht gefordert — die Transportmatrix ist total unimodular, alle Ecken sind ganzzahlig.
8.4 — Zuordnung mit Verboten. Kosten auf einen sehr hohen Wert setzen (kosten[2][2] = 1e6) oder mit np.inf arbeiten (bei linear_sum_assignment erlaubt). Die Lösung weicht auf die zweitbeste Zuordnung für Carla aus; die Gesamtkosten steigen um die Differenz.
8.5 — Engpass finden. Die Dualwerte der Kapazitätsschranken (res.upper.marginals bei linprog) zeigen es direkt; alternativ jede Kapazität einzeln um 1 erhöhen und neu rechnen. Im Beispiel ist die Kante Werk_A → Umschlag der Engpass (voll ausgelastet, günstigster Weg).
-
8.6 — VRP variieren. (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald \text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf} (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) oder wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die Metaheuristik verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
-
8.7 — TSP mit MTZ. Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während OR-Tools weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
+
8.6 — VRP variieren. (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald \text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf} (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) oder wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die Metaheuristik verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
+
8.7 — TSP mit MTZ. Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während OR-Tools weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
Finde den Denkfehler — Die vergessene Dimension
Was die Fahrzeuge tun. Fahrzeuge 1, 2 und 3 fahren gar nicht — sie stehen mit null Stopps im Depot. Fahrzeug 4 bedient alle 16 Kunden und lädt dabei 37 Paletten bei einer Kapazität von 10. Es ist um 270 % überladen. Physikalisch ist dieser Plan nicht ausführbar; im Modell ist er die beste Lösung.
@@ -694,12 +694,12 @@
Es fehlt die Schranke.
Alles, was der Kollege sagt, stimmt. Der Fehler liegt darin, was er nicht sagt: Die Ersparnis wird gegen die bisherige Praxis gemessen, nicht gegen das Mögliche. Damit beantwortet die Zahl eine andere Frage als die, die das Management stellt.
Metaheuristik_vs_Exakt.py liefert die fehlende Zahl in der Spalte Schranke: 1 768 Minuten. Der Abstand der vorgestellten Lösung zum Bestmöglichen beträgt also bis zu 25 %, nicht 0 %. In derselben Nacht, in der 640 Stunden gefeiert werden, liegen möglicherweise weitere 2 000 Stunden ungenutzt herum.
-
Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist LNS.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
+
Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist LNS.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
Dasselbe Muster in anderer Verkleidung: Kapitel Handelsmaschine, Data_Snooping.py — dort fehlt nicht die Schranke, sondern die Zahl der Versuche. Beide Male macht eine weggelassene Kennzahl aus einem korrekten Ergebnis eine irreführende Aussage.
Micro-Quiz
-
1. b) Eine lokale Suche lebt von der Zahl geprüfter Züge. Die volle Summe kostet O(n) statt O(1) — bei 500 Aufträgen zwei Größenordnungen weniger Züge im selben Zeitbudget. Numerisch ist an der vollen Summe nichts falsch (a), und die Konvergenzaussagen zu Simulated Annealing hängen nicht an der Bewertungsfunktion (c).
+
1. b) Eine lokale Suche lebt von der Zahl geprüfter Züge. Die volle Summe kostet O(n) statt O(1) — bei 500 Aufträgen zwei Größenordnungen weniger Züge im selben Zeitbudget. Numerisch ist an der vollen Summe nichts falsch (a), und die Konvergenzaussagen zu Simulated Annealing hängen nicht an der Bewertungsfunktion (c).
2. b) Die angenommenen Verschlechterungen summieren sich. 0,29 % von mehreren hunderttausend Zügen sind einige hundert angenommene Verschlechterungen zu je rund 70 Minuten — genug, um die Suche auf das 2,1-fache der Startlösung zu tragen. Die Annahmewahrscheinlichkeit sinkt im Lauf (a ist falsch), und numerisch instabil ist nichts (c).
3. b) Die untere Schranke. Sie ist unabhängig davon, wie gut die gefundene Lösung ist, und die einzige verfügbare Aussage darüber, wie viel Luft noch nach oben ist. (c) klingt plausibel, ist hier aber falsch: Die CP-SAT-Lösung war schlechter als die Faustregel und damit als Startlösung unbrauchbar.
Selbsttest
@@ -754,14 +754,14 @@
Der Fehler steckt in der Vorstellung, „aufrunden” koste höchstens eine Rolle. Aufgerundet wird nicht eine Zahl, sondern dreizehn — je Muster eine. Jede einzelne Aufrundung kostet bis zu eine Rolle, und in der Summe sind es sechs.
-
Die Schranke sagt: Weniger als 73 Rollen sind unmöglich. Sie sagt nicht: Jede zulässige Lösung liegt höchstens eine Rolle darüber. Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap in Abschnitt 6.8, nur in die andere Richtung.
+
Die Schranke sagt: Weniger als 73 Rollen sind unmöglich. Sie sagt nicht: Jede zulässige Lösung liegt höchstens eine Rolle darüber. Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap in Abschnitt 6.8, nur in die andere Richtung.
Was der Kollege stattdessen hätte tun müssen: das Master ein zweites Mal lösen, diesmal mit integrality=1 über genau die Spalten, die er ohnehin schon erzeugt hat. Das ist eine Zeile, dauert Millisekunden und liefert 73 statt 79. Er hatte alles dafür bereits vorliegen.
🎯 Die allgemeine Lehre Eine gebrochene LP-Lösung ist kein Plan, sondern eine Schranke. Der Weg zur ganzzahligen Lösung führt über den Solver, nicht über ceil().
Micro-Quiz
1. b) Das Mustermodell kennt keine einzelnen Rollen und damit keine austauschbaren Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund; (c) ist falsch, die LP-Lösung ist hier gerade nicht ganzzahlig (72,92).
-
2. b) Ein Rucksackproblem: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
+
2. b) Ein Rucksackproblem: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
3. b) Die untere Schranke von 33,60 beweist, dass mindestens 34 Rollen nötig sind. Damit weiß man, dass die 35 der Faustregel höchstens eine daneben liegen — und kann aufhören zu suchen. Genau diese Aussage kann eine Heuristik allein nie liefern (Kapitel 9).
Selbsttest
@@ -774,8 +774,8 @@
A.11 Lösungen zu Kapitel „Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität“
11.1 — Konvexität einordnen. PSD (alle \ge 0) → konvex, aber nicht streng konvex; die Lösung ist nicht notwendig eindeutig. Der Eigenwert 0 bedeutet eine flache Richtung: Entlang des zugehörigen Eigenvektors ändert sich der quadratische Term nicht — es gibt eine Rinne statt eines Punktes.
-
11.2 — Komplementärer Schlupf. Ja, verträglich: \lambda_i w_i = 0 gilt für alle i (w_2 = 0 mit \lambda_2 > 0; die anderen mit \lambda = 0). \lambda_2 = 0{,}03 bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort bindend.
11.2 — Komplementärer Schlupf. Ja, verträglich: \lambda_i w_i = 0 gilt für alle i (w_2 = 0 mit \lambda_2 > 0; die anderen mit \lambda = 0). \lambda_2 = 0{,}03 bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort bindend.
11.4 — Unmögliche Korrelationsmatrix. Eigenwerte \approx (-0{,}62;\ 1{,}0;\ 2{,}62) → nicht PSD, also unmöglich. Anschaulich: Wenn A stark positiv mit B korreliert und B stark positiv mit C, kann A nicht gleichzeitig stark negativ mit C korrelieren — Korrelation ist eingeschränkt transitiv. Formal: \rho_{AC} \ge \rho_{AB}\rho_{BC} - \sqrt{(1-\rho_{AB}^2)(1-\rho_{BC}^2)} = 0{,}81 - 0{,}19 = 0{,}62; verlangt waren -0{,}9.
11.5 — Gewichtung untersuchen. Erwartetes Muster: Größeres \alpha → mehr Rendite, mehr Konzentration im Titel mit höchstem \mu. Größeres \beta → gleichmäßigere Gewichte, Entropie steigt Richtung \ln 4 = 1{,}386. Ab etwa \beta \approx 0{,}1 dominiert die Entropie und man nähert sich der Gleichgewichtung. Empfehlung an einen Ausschuss: Nicht mit \beta argumentieren, sondern mit der resultierenden Maximalposition — „mit dieser Einstellung liegt keine Position über 40 %“ ist verständlich, „\beta = 0{,}015“ nicht.
11.6 — Nicht-Konvexität demonstrieren. Mit der bewusst fehlerhaft konstruierten Matrix finden 20 Startpunkte typischerweise mehrere verschiedene Optima; die „Portfoliovarianz“ w^\top\Sigma w kann bei geeigneten Gewichten negativ werden (der zugehörige Eigenvektor liegt allerdings teilweise außerhalb des zulässigen Bereichs w \ge 0{,}001, \sum w = 1 — deshalb fällt der Fehler bei naiver Prüfung nicht auf).
@@ -807,14 +807,14 @@
Prüfen, ob die Matrix positiv semidefinit ist (Zeile aus (b)) — beim Einlesen, nicht beim Optimieren.
Die Ursache suchen. Korrelationen aus verschiedenen Quellen, Zeiträumen oder Frequenzen sind fast nie widerspruchsfrei. Schätzen Sie die Matrix aus einem konsistenten Datensatz.
-
Ist das nicht möglich, die Matrix reparieren: negative Eigenwerte auf null setzen und rekonstruieren (Eigenwert-Clipping, siehe die Aufgabe Konvexität einer Kovarianzmatrix reparieren in Kapitel 2) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert (Kapitel 18).
+
Ist das nicht möglich, die Matrix reparieren: negative Eigenwerte auf null setzen und rekonstruieren (Eigenwert-Clipping, siehe die Aufgabe Konvexität einer Kovarianzmatrix reparieren in Kapitel 2) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert (Kapitel 18).
Der Merksatz dazu: Eine Kovarianzmatrix ist kein Behälter für einzeln geschätzte Zahlen, sondern ein geometrisches Objekt. Nicht jede Kombination von Korrelationen existiert.
Micro-Quiz
1 — (b). Der DCPError ist eine inhaltliche Aussage: Für diese Formulierung kann es keine Optimalitätsgarantie geben. Auf scipy auszuweichen (a) beseitigt die Meldung, nicht die Ursache — man bekommt dann ein lokales Ergebnis ohne Garantie und ohne Warnung, wie Abschnitt 11.8 zeigt. Toleranzen (c) haben mit Konvexität nichts zu tun. Richtig ist, zwischen zwei bewussten Wegen zu wählen: konvex umformulieren, oder lokal rechnen und das im Bericht kenntlich machen.
-
2 — (b) ein lokales Minimum.success: True beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von SLSQP eingehalten.
+
2 — (b) ein lokales Minimum.success: True beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von SLSQP eingehalten.
3 — (c) Schattenpreis.\lambda^* ist die Ableitung des optimalen Zielwerts nach der rechten Seite der Nebenbedingung — dieselbe Bedeutung wie der Dualwert im LP (Kapitel 5), nur für allgemeine, auch krumme Nebenbedingungen. (a) und (b) verwechseln den Multiplikator mit einer Verletzungszahl beziehungsweise mit dem Schlupf; der Schlupf ist bei einer bindenden Bedingung gerade null (komplementärer Schlupf).
Selbsttest
@@ -827,12 +827,12 @@
A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“
12.1 — Fluch des Durchschnitts. Weil sich Verzögerungen fortpflanzen und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (der nächste Schritt ist noch nicht bereit), ein verspätetes verzögert alles. Weitere Beispiele: Wartezeiten in Warteschlangen (nichtlinear in der Auslastung), Projektkosten mit Nachträgen.
-
12.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
+
12.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
12.3 — Zweistufig rechnen. (a) Mit Spot = 60: Ableitung bei x \in (100,250): 40 - 60\cdot0{,}5 + 5\cdot0{,}5 = 40-30+2{,}5 = +12{,}5 > 0 → Kapazität senken. Bei x < 100: 40 - 60 = -20 < 0 → erhöhen. Optimum daher x^* = 100. (b) Billige Nachbesserung macht Vorhalten unattraktiv. (c) Bei x = 225 optimal müsste die Ableitung dort das Vorzeichen wechseln — das ist bei diskreten Szenarien nur zufällig der Fall; der Mittelwert ist nur bei symmetrischen Kosten und stetiger Verteilung optimal.
-
12.4 — EVPI interpretieren. Selbst eine perfekte Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also niemals rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
+
12.4 — EVPI interpretieren. Selbst eine perfekte Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also niemals rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
12.5 — Monte-Carlo erweitern. (a) Servicelevel 95 %: Kapazität = 95 %-Quantil des Bedarfs (ca. 485). Die Zusatzkosten gegenüber dem Kostenoptimum (180) betragen ein Vielfaches — Servicelevel ist teuer, und genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (b) cvar = kosten[kosten >= np.quantile(kosten, 0.95)].mean(). (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben.
12.6 — Den Preis der Zusage selbst bestimmen. (a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die Norm, tragen zur gesicherten Leistung aber kaum bei. (b) Der Unterschied ist nicht numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert selbst der Vollausbau in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese Fälle in den Daten und meldet korrekt infeasible, sobald die Zusage über 92 % steigt. Das analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet optimal — und der Plan hält gemessen 87,4 %. Glauben sollte man dem Szenariomodell: Ein infeasible ist hier die richtige Antwort. Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. (c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“
\Gamma = 0 entspricht dem nominalen Fall, \Gamma = n dem vollen Worst Case. Dazwischen steuert \Gamma die Vorsicht stufenlos — der praktisch nützlichste Bereich liegt meist bei \Gamma \approx \sqrt{n}.
Anschaulich: Das Projekt ist fertig, wenn das letzte Gewerk fertig ist. Ein Gewerk, das statt 11 nur 7 Tage braucht, bringt niemandem etwas — die anderen sind ja noch dran. Ein Gewerk, das 17 Tage braucht, hält alle auf. Die guten Ausreißer verpuffen, die schlechten schlagen voll durch.
-
Damit gilt für das Maximum immer \mathbb{E}[\max] \ge \max(\mathbb{E}). Die Differenz ist kein Schätzfehler, sondern eine systematische Verzerrung — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
+
Damit gilt für das Maximum immer \mathbb{E}[\max] \ge \max(\mathbb{E}). Die Differenz ist kein Schätzfehler, sondern eine systematische Verzerrung — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
Warum 95,5 %. Damit das Projekt die geplanten 11,33 Tage hält, müssen alle fünf Gewerke gleichzeitig ihren Mittelwert unterbieten. Für die Dreiecksverteilung (6;\ 10;\ 18) ist die Wahrscheinlichkeit dafür je Gewerk
Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauert 17 Tage“, sondern: „In 9 von 10 Fällen sind wir nach 17 Tagen fertig; im Mittel nach 14,4.“ Diese zwei Zahlen sind eine belastbare Zusage — eine einzelne ist es nie.
Micro-Quiz
1 — (b). Das kritische Verhältnis 1400/(1400+120) = 0{,}921 gibt an, wie weit man sich auf die günstigere Fehlerseite stellen soll: Bestellt wird das 92,1-%-Quantil des Bedarfs, hier 38 Stück. (a) ignoriert die Kostenasymmetrie und kostet im Kapitelbeispiel 160 % mehr. (c) verwechselt die Fragestellung: Die Kapitalbindung ist bereits in den 120 € je überzähligem Stück enthalten — sie rechtfertigt keine zusätzliche Kürzung.
-
2 — (b). Der Unterschied liegt darin, was man wissen muss: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: Robuste Optimierung betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
+
2 — (b). Der Unterschied liegt darin, was man wissen muss: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: Robuste Optimierung betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
3 — (c). Bei durchgehender Linearität und ohne nachgelagerte Entscheidung gilt \mathbb{E}[f(X)] = f(\mathbb{E}[X]) — dann ist Rechnen mit Mittelwerten korrekt. Sobald aber ein Maximum, ein Minimum, ein Betrag oder eine Nachbesserungsentscheidung auftaucht, gilt das nicht mehr: Genau das sind die beiden Fälle aus Schnellstart (asymmetrische Kosten über maximum) und Abschnitt 12.9 (Maximum über parallele Vorgänge). (a) ist die Fehlannahme, um die es im ganzen Kapitel geht; (b) ist zu absolut — es gibt den linearen Fall, in dem es tatsächlich zulässig ist.
A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“
13.1 — Bausteine. Stufe = Tag 1–5; Zustand = verbleibende Distanz (ggf. plus Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung. Nicht in den Zustand gehören: bereits gelaufene Kilometer (redundant, wenn die Restdistanz bekannt ist) und das Wetter von gestern (ohne Einfluss auf die Zukunft).
-
13.2 — Optimalitätsprinzip. Weil der Wert eines Zustands nur von den künftigen Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von früheren Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit in den Zustand aufnehmen (dann wächst allerdings der Zustandsraum).
-
13.3 — Rückwärtsinduktion. Mit C(n) = n^2 + 2n und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad 2 \to 2 \to 1 (oder eine Permutation davon) mit Gesamtkosten 8 + 8 + 3 = 19. Alles auf einmal kostet 25 + 10 = 35.
-
13.4 — Rucksack als DP.
+
13.2 — Optimalitätsprinzip. Weil der Wert eines Zustands nur von den künftigen Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von früheren Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit in den Zustand aufnehmen (dann wächst allerdings der Zustandsraum).
+
13.3 — Rückwärtsinduktion. Mit C(n) = n^2 + 2n und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad 2 \to 2 \to 1 (oder eine Permutation davon) mit Gesamtkosten 8 + 8 + 3 = 19. Alles auf einmal kostet 25 + 10 = 35.
V = np.zeros((n +1, kapazitaet +1))for i inrange(1, n +1):for c inrange(kapazitaet +1):
@@ -944,7 +944,7 @@ F(11{,}33) = 1 - \frac{(18-11{,}33)^2}{(18-6)(18-10)} = 1 - \frac{44{,}4}{96} =
... kosten = (RUESTKOSTEN if (p >0andnot lief_vorher) else0) + ... rest, plan = V(t +1, neu, p >0)
-
Damit findet die Rückwärtsinduktion (3, 1, 4, 2) für 70 € — das nachgerechnete Optimum. Der Zustandsraum verdoppelt sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der Fluch der Dimensionalität.
+
Damit findet die Rückwärtsinduktion (3, 1, 4, 2) für 70 € — das nachgerechnete Optimum. Der Zustandsraum verdoppelt sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der Fluch der Dimensionalität.
Warum der richtige Betrag das Tückische ist. Man würde erwarten, dass ein falsches Modell auch einen falschen Kostenbetrag ausgibt — dann fiele es beim Nachrechnen auf. Hier nicht: Für den von ihm gewählten Plan (5,0,5,0) stimmt die Rechnung zufällig, weil in diesem Plan tatsächlich jede Produktionsperiode auf eine Pause folgt. Modellannahme und Wirklichkeit fallen für genau diese eine Lösung zusammen.
Begründung in einem Satz: „Für 646 Euro sparen wir 843 Kilogramm CO₂ — zu 77 Cent je Kilogramm, während wir intern mit 90 Cent rechnen.“
14.3 — Mehr Trassen. Mit 8 statt 5 Trassen wird der Engpass schwächer. Zu erwarten ist eine kürzere Front: Der Zielkonflikt entsteht ja allein aus der Knappheit; je mehr Trassen, desto näher rücken Kostenminimum und CO₂-Minimum zusammen. Im Grenzfall genügend vieler Trassen fallen beide zusammen — die Bahn ist billiger und sauberer, also gibt es gar keinen Konflikt mehr und die Front schrumpft auf einen Punkt.
Der Lehrpunkt: Ein Zielkonflikt ist keine Eigenschaft der Ziele, sondern der Knappheit. Wer ihn auflösen will, sollte zuerst prüfen, ob sich die Ressource vermehren lässt, statt über Gewichte zu verhandeln.
-
14.4 — Die Front der Relaxation. Ohne integrality ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also keinen Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
+
14.4 — Die Front der Relaxation. Ohne integrality ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also keinen Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
Genau das ist der Grund, warum das Problem in Lehrbüchern zur linearen Programmierung nicht vorkommt und in der betrieblichen Praxis ständig: Sobald „welcher Träger“, „welches Lager“ oder „welche Schicht“ entschieden wird, zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen die Einbuchtungen, in denen die nicht gestützten Lösungen liegen.
14.5 — Drei Ziele. Die Front wird zu einer Fläche im dreidimensionalen Zielraum. Das ε-Verfahren braucht dann ein Gitter über zwei Schranken statt einer Folge über eine — der Aufwand wächst von O(k) auf O(k^2), bei m Zielen auf O(k^{m-1}).
Zwei praktische Folgerungen: Erstens ist bei drei und mehr Zielen die vollständige Front meist nicht mehr bezahlbar; man arbeitet dann mit einer Stichprobe oder mit Metaheuristiken, die eine Front approximieren (NSGA-II, in pymoo enthalten). Zweitens ist eine Front mit hunderten Punkten für die Entscheidung ohnehin unbrauchbar — ab drei Zielen ist die bessere Frage meist, ob sich zwei davon zusammenfassen lassen.
Das ist die richtige Richtung und leicht zu merken: Je billiger der Überhang, desto großzügiger darf man ansetzen. Im Grenzfall kostenloser Entsorgung (c_+ \to 0) geht das kritische Verhältnis gegen 1 — man bestellt so viel, dass praktisch nie etwas fehlt.
15.2 — Wann ist der Mittelwert richtig? Nur wenn c_- = c_+und die Nachfrageverteilung symmetrisch ist. Dann liegt das kritische Verhältnis bei 0,5, das 50-%-Quantil ist der Median, und bei Symmetrie fällt der Median mit dem Mittelwert zusammen.
Beide Bedingungen sind unrealistisch. Fehlmenge und Überhang kosten fast nie dasselbe — bei Frischware ist der Überhang teurer, bei Ersatzteilen die Fehlmenge um Größenordnungen. Und Nachfrageverteilungen sind meist rechtsschief. Der Mittelwert ist damit die Ausnahme, nicht die Regel — er wird nur benutzt, weil Regressionsmodelle ihn standardmäßig liefern.
-
15.3 — Die Kennzahl der Prognoseabteilung. Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim MAPE, weil beide Maße den Erwartungswert belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
+
15.3 — Die Kennzahl der Prognoseabteilung. Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim MAPE, weil beide Maße den Erwartungswert belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
Das ist kein Zufall, sondern der Kern des Kapitels: Jedes rein statistische Fehlermaß ignoriert die Kostenasymmetrie. Man kann das Problem nicht lösen, indem man das Fehlermaß wechselt; man muss die Kosten selbst messen. (Die einzige Ausnahme ist der Pinball Loss — genau das Maß, das die Quantilregression minimiert, und das ist eben kein allgemeines Prognosemaß, sondern eines für ein bestimmtes Quantil.)
15.4 — Zwei Quantile. Das Band ist an Aktionstagen deutlich breiter — dort ist die Streuung im Modell fast viermal so groß. Genau das ist die Information, die eine Punktprognose plus fester Zuschlag wegwirft.
Ein praktischer Nebeneffekt: Ein solches Band ist die verständlichste Form, Unsicherheit an die Disposition zu berichten. „Zwischen 130 und 210” sagt einem Menschen mehr als „170 ± Sicherheitszuschlag”.
-
15.5 — Der Wert der Merkmale. Zu erwarten ist, dass die Kosten stärker steigen als der MSE. Der Grund: Das Merkmal „Aktion” trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) und die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
+
15.5 — Der Wert der Merkmale. Zu erwarten ist, dass die Kosten stärker steigen als der MSE. Der Grund: Das Merkmal „Aktion” trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) und die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
Der Lehrsatz dahinter: Der Wert eines Merkmals hängt davon ab, wofür man es benutzt. Eine Merkmalsauswahl, die nach MSE-Beitrag sortiert, wirft möglicherweise genau die Merkmale weg, die für die Entscheidung am wichtigsten sind — nämlich die, die etwas über die Unsicherheit sagen.
Finde den Denkfehler — „Wir haben die Prognose um 18 % verbessert“
Der Sicherheitszuschlag stammt aus den Residuen des alten Modells.
16.3 — Die Brücke rückwärts. Shrinkage hilft, wenn man eine Kovarianzmatrix aus wenigen Beobachtungen schätzt. Bei Lieferzeiten tritt genau dasselbe Problem auf, sobald man die Korrelationen zwischen Lieferanten braucht — und die braucht man, sobald Lieferanten gemeinsame Ursachen haben: derselbe Hafen, dasselbe Vorprodukt, dieselbe Region.
Bei 40 Lieferanten hat die Kovarianzmatrix 820 zu schätzende Einträge. Wer dafür 36 Monatswerte hat, schätzt 820 Zahlen aus 36 Beobachtungen — dasselbe Missverhältnis wie bei Aktienrenditen, mit denselben Folgen (Kovarianz_Falle.py).
Benötigt würden: Lieferzeit-Zeitreihen je Lieferant über denselben Zeitraum, gleich getaktet. Genau daran scheitert es in der Praxis meist — die Daten liegen in Bestellvorgängen, nicht in einer Matrix.
-
16.4 — CVaR mit Ganzzahligkeit. Man ergänzt Binärvariablen y_j je Lieferant und die Kopplung w_j \le y_j sowie \sum_j y_j \le 3. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt cp.Variable(n, boolean=True).
+
16.4 — CVaR mit Ganzzahligkeit. Man ergänzt Binärvariablen y_j je Lieferant und die Kopplung w_j \le y_j sowie \sum_j y_j \le 3. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt cp.Variable(n, boolean=True).
Zu erwarten ist ein höherer CVaR: Der zulässige Bereich wird kleiner, und die Feinabstimmung über sechs Lieferanten fällt weg. Der Aufschlag ist der Preis der Vertragswirklichkeit — und genau die Zahl, die man dem Einkauf vorlegt, wenn dort jemand sagt, drei Lieferanten seien genug.
16.5 — Die eigene Brücke. Freie Antwort. Eine gute Bearbeitung nennt das Modell, die Entsprechung und die Zeile der Grenztabelle, die im Weg steht — meist ist es die dritte (Teilbarkeit) oder die erste (gemessen gegen geschätzt). Wenn keine im Weg steht, ist die Übertragung wahrscheinlich zu oberflächlich geprüft.
Finde den Denkfehler — „Das ist doch dasselbe Problem“
Ein LP sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
+
Ein LP sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
„Stellen Sie sich vor, Ihre Engpassmaschine wäre nicht die Fräse, sondern eine Vorschrift: Sie dürfen nur eine bestimmte Menge Risiko in den Büchern haben. Der Schattenpreis sagt dann, was eine Lockerung dieser Vorschrift wert wäre — genau wie bei einer zusätzlichen Maschinenstunde.”
Fette Ränder (seltene, aber sehr große Abweichungen). Die Standardabweichung mittelt sie weg und bestraft außerdem Abweichungen nach oben genauso wie nach unten; CVaR sieht ausschließlich auf den schlechten Rand.
Log-Renditen sind Differenzen von Logarithmen — die addieren sich über die Zeit. Diskrete Renditen sind lineare Anteile am Kapital — die addieren sich über gewichtete Positionen.
Die Optimierung sucht die Richtungen kleinster geschätzter Varianz — genau jene, deren Eigenwerte am stärksten nach unten verzerrt sind. Sie optimiert dadurch in das Schätzrauschen hinein.
-
Der Rang von \mathbf{X}^\top\mathbf{X} ist höchstens T < N — die Matrix ist nicht invertierbar, das GMV-Problem hat unendlich viele Lösungen.
+
Der Rang von \mathbf{X}^\top\mathbf{X} ist höchstens T < N — die Matrix ist nicht invertierbar, das GMV-Problem hat unendlich viele Lösungen.
Stichprobenmatrix (unverzerrt, verrauscht) mit strukturiertem Ziel (verzerrt, stabil). \delta wird analytisch so bestimmt, dass der erwartete quadratische Fehler minimal wird.
Skalierte Einheitsmatrix (sklearn) und Konstant-Korrelations-Ziel (LW 2003).
Beste Sharpe Ratio unter den vier Kandidaten: (0{,}3;\ 0{,}7). Das exakte Optimum liegt bei w_1 = 0{,}318. Bemerkenswert: Die Mischung (0{,}3;\ 0{,}7) hat mit 10,08 % eine geringere Volatilität als beide Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
+
Beste Sharpe Ratio unter den vier Kandidaten: (0{,}3;\ 0{,}7). Das exakte Optimum liegt bei w_1 = 0{,}318. Bemerkenswert: Die Mischung (0{,}3;\ 0{,}7) hat mit 10,08 % eine geringereVolatilität als beide Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
19.2 — Lambda deuten. Von \lambda=0 (GMV, linker unterer Punkt der Kurve) wandert die Lösung entlang der Effizienzgrenze nach rechts oben, bis sie bei \lambda\to\infty im Titel mit der höchsten Rendite endet (bzw. an der Positionsobergrenze).
-
19.3 — Korn-Transformation.\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}} — bei einem Portfolio mit \sum w_i = 1 und Überrenditen geschrieben als \mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1}) kürzt sich c heraus. Mit der Bedingung \sum w_i = 1 ist die Skala jedoch fixiert, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch \sum y_i = \kappa mit freiem \kappa und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
+
19.3 — Korn-Transformation.\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}} — bei einem Portfolio mit \sum w_i = 1 und Überrenditen geschrieben als \mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1}) kürzt sich c heraus. Mit der Bedingung \sum w_i = 1 ist die Skala jedoch fixiert, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch \sum y_i = \kappa mit freiem \kappa und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
19.4 — Restriktionen kosten. Erwartetes Muster: Sharpe Ratio sinkt monoton mit strengerer Grenze. Unlösbar wird es bei w_{\max} < 1/n — dann kann die Summe der Gewichte 1 nicht mehr erreicht werden.
19.5 — Den Sektorfehler nachstellen. (a) AAPL, AMZN, CVX, GS statt AAPL, MSFT, NVDA, AMZN. (b) Die Gewichte unterscheiden sich deutlich, weil die eigentlich zu begrenzenden Tech-Titel frei laufen. (c) Nein — die Kennzahlen sehen völlig plausibel aus. Genau das macht den Fehler so gefährlich.
19.6 — Kardinalität. Erwartung: Die Sharpe Ratio sinkt leicht, die Rechenzeit steigt deutlich (MIQP statt QP). Bei K \ge 5 und w_{\max} = 0{,}20 ist die Restriktion praktisch nicht mehr bindend.
Und die Kontrollfrage für den Alltag: Rechnen Sie Ihr Modell mit den Daten des halben Zeitraums und dann mit denen der anderen Hälfte. Wenn die Gewichte dabei stark springen, optimieren Sie Rauschen — unabhängig davon, wie gut die Kennzahlen im Schätzzeitraum aussehen.
Micro-Quiz
-
1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
+
1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
2 — (b) es braucht keine Renditeschätzung. Renditen sind die mit Abstand unzuverlässigste Eingangsgröße (Standardfehler größer als der geschätzte Wert selbst); wer sie nicht benötigt, umgeht das Problem vollständig. (a) trifft nicht zu — die Nebenbedingungen sind dieselben. (c) ist falsch: Das Minimum-Varianz-Portfolio erzielt erwartungsgemäß weniger Rendite; sein Vorteil liegt in der Verlässlichkeit, nicht in der Höhe.
3 — (b) Schätzfehler. Eine Konzentration von 44 % auf einen von zwölf Titeln ist das typische Bild eines Optimierers, der einem Rauschsignal folgt — im Kapitelbeispiel entsteht sie sogar dann, wenn alle Anlagen nachweislich identisch sind. (a) mag zutreffen, ist aber die unwahrscheinlichere Erklärung und muss belegt werden, nicht angenommen. (c) beschriebe ein anderes Fehlerbild: Eine falsch skalierte Kovarianzmatrix führt zu unplausiblen Risikowerten, nicht zu Konzentration.
A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“
-
20.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \text{VaR}_{80\%} = 2{,}1. $_{80%} = $ Mittel der schlechtesten 20 % = (5{,}4+8{,}9)/2 = \mathbf{7{,}15}.
-
20.2 — Subadditivität. Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
+
20.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \text{VaR}_{80\%} = 2{,}1. $_{80%} = $ Mittel der schlechtesten 20 % = (5{,}4+8{,}9)/2 = \mathbf{7{,}15}.
+
20.2 — Subadditivität. Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
20.3 — Rockafellar-Uryasev nachvollziehen. (a) Schlechtestes Drittel von (1,4,9) ist \{9\} → CVaR = 9. (b) Mit \frac{1}{S(1-\alpha)} = \frac{1}{3\cdot(1/3)} = 1: \gamma=0: 0 + (1+4+9) = 14. \gamma=1: 1 + (0+3+8) = 12. \gamma=4: 4 + (0+0+5) = \mathbf{9}. \gamma=5: 5+4 = 9. \gamma=9: 9+0 = 9. (c) Minimum ab \gamma = 4 bei 9 — identisch mit (a) ✓. (Das Minimum wird auf einem ganzen Intervall angenommen, weil die Funktion stückweise linear ist — genau der Grund, warum der CVaR nicht streng konvex ist.)
20.4 — Einheiten prüfen. In einem Modell, das annualisierte Rendite gegen täglichen CVaR verrechnet, wirkt 1{,}5 effektiv als 1{,}5/252 \approx 0{,}006 auf Tagesbasis — der Risikoterm ist also um Faktor 252 zu leicht gewichtet. Um dieselbe Wirkung wie RISIKOAVERSION = 1.5 im konsistenten Tagesmodell zu erzielen, hätte man dort lambda_risk = 1.5 * 252 = 378 setzen müssen.
20.5 — Risikoaversion kalibrieren. (a) Konkav, ähnlich der Markowitz-Frontier, aber im Rendite-CVaR-Raum. (b) Bei Normalverteilung entspricht CVaR-Optimierung ungefähr der Varianz-Optimierung; die Ergebnisse divergieren umso stärker, je schiefer die Verteilung ist. (c) Empfehlung ohne Fachjargon: „Bei dieser Einstellung liegt der durchschnittliche Verlust an den schlechtesten fünf Prozent der Tage bei X Prozent — bei einer erwarteten Rendite von Y Prozent.“ Das ist entscheidbar; „\lambda = 4“ ist es nicht.
Warum der CVaR das nicht kann. Der CVaR mittelt über den gesamten Schwanz, statt an seiner Grenze stehenzubleiben. Damit sieht er den Ausfall in jedem der drei Fälle, und das Ergebnis verhält sich wie erwartet: 79,59 € und 79,66 € einzeln, 101,33 € zusammen statt 159,24 € — die Diversifikation senkt das Risiko um rund ein Drittel.
Formal folgt die Subadditivität daraus, dass der CVaR sich als Maximum über Erwartungswerte schreiben lässt (Darstellungssatz für kohärente Risikomaße), und Maxima von Erwartungswerten sind stets subadditiv. Anschaulicher: Ein Mittelwert über eine Menge verhält sich gutartig, wenn man Mengen zusammenlegt; ein Quantil nicht — es kann springen, sobald sich die Reihenfolge der Szenarien ändert.
-
Die praktische Konsequenz ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den Expected Shortfall — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
+
Die praktische Konsequenz ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den Expected Shortfall — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
Micro-Quiz
1 — (b) nichts. Der VaR ist ein Quantil: Er markiert die Schwelle und sagt nichts über den Bereich dahinter. Genau das zeigt der Schnellstart dieses Kapitels — zwei Anlagen mit identischem VaR von 3,00 %, aber CVaR 3,00 % gegen 9,17 %. (a) verwechselt die Schwelle mit dem, was hinter ihr liegt. (c) ist ebenfalls falsch: Anlage B hat zwar die höhere Schwankung, aber die Schwankung allein sagt nichts über die Form des Schwanzes — genau deshalb reicht auch die Varianz als Risikomaß nicht aus.
2 — (b). Rockafellar und Uryasev zeigen, dass sich der CVaR als Minimum über eine Hilfsvariable \gamma schreiben lässt; mit Schlupfvariablen für die Terme \max(\cdot, 0) wird daraus ein gewöhnliches LP. Die VaR-Minimierung ist dagegen nicht-konvex (das Quantil springt) und hat viele lokale Optima — dasselbe Problem wie in Abschnitt 11.6. (a) ist zwar zutreffend, aber nicht der entscheidende Grund; (c) ist eine wahre Aussage ohne Bezug zur Optimierbarkeit.
A.21 Lösungen zu Kapitel „Die vollständige quantitative Handelsmaschine“
-
21.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) Survivorship-Bias — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
+
21.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) Survivorship-Bias — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
21.2 — Kennzahlen deuten. A: Sharpe = (12-2)/22 = 0{,}45, Calmar = 12/35 = 0{,}34. B: Sharpe = (8-2)/9 = 0{,}67, Calmar = 8/12 = 0{,}67. Empfehlung: B — für einen Pensionsfonds ist der Drawdown entscheidend, weil laufende Auszahlungen in einer Verlustphase Substanz vernichten. B ist in beiden risikoadjustierten Maßen besser.
-
21.3 — Rebalancing-Kalender. Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
+
21.3 — Rebalancing-Kalender. Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
21.4 — Rebalancing-Frequenz. Typisches Muster: Turnover und Kosten steigen etwa linear mit der Frequenz, der Bruttoertrag verbessert sich nur unterproportional. Bei 0,15 % Gebühren ist monatlich meist vertretbar, bei 0,5 % eher quartalsweise. Ein Toleranzband (nur handeln bei Abweichung > x %) schlägt fast immer die feste Frequenz.
21.5 — Krisenverhalten. Erwartung: Die Überrendite ist selten stabil; oft stammt sie aus wenigen Perioden. Folgerung: Eine gute Gesamtkennzahl kann von einer einzigen glücklichen Phase getragen sein — immer nach Teilzeiträumen aufschlüsseln.
-
21.6 — Data Snooping messen. Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die Differenz zwischen (a) und (c) ist der Selektionseffekt — genau das, was die Deflated Sharpe Ratio korrigieren soll.
+
21.6 — Data Snooping messen. Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die Differenz zwischen (a) und (c) ist der Selektionseffekt — genau das, was die Deflated Sharpe Ratio korrigieren soll.
Finde den Denkfehler — Die Strategie mit dem hochsignifikanten Ergebnis
-
Warum ein korrekter Backtest wertlos sein kann. Der Backtest misst genau das, was er messen soll: die Wertentwicklung dieser einen Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: „Ist dieses Ergebnis besser, als es Zufall erklären kann?“
+
Warum ein korrekter Backtest wertlos sein kann. Der Backtest misst genau das, was er messen soll: die Wertentwicklung dieser einen Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: „Ist dieses Ergebnis besser, als es Zufall erklären kann?“
Und für diese Frage ist entscheidend, dass die Strategie als Beste aus hundert ausgewählt wurde. Ein p-Wert von 0,0094 bedeutet: „Wenn diese Strategie keinen Vorteil hätte, sähe sie in 0,94 % der Fälle so gut aus.“ Bei hundert Versuchen erwartet man aber rund fünf Ergebnisse unter dem 5-%-Niveau — allein durch Zufall. Genau das zeigt die Tabelle: Auf reinem Rauschen liefert die Beste von hundert im Mittel Sharpe 1,13 und p = 0,0094. Dieselben Zahlen.
Der Fehler steckt nicht im Backtest, sondern in der Auswahl. Deshalb ist er auch durch noch so sorgfältiges Programmieren nicht zu verhindern.
1 — (b) das Zeitlimit greift. Eine Laufzeit, die exakt dem Limit entspricht und über Monate konstant bleibt, ist kein Stabilitätsbeleg, sondern zeigt, dass der Solver jedes Mal abgebrochen wird. (a) ist die Fehldeutung, um die es im Denkfehler geht. (c) läge nahe, wenn die Laufzeit schwankte oder das Limit überschritte — hier ist die Ursache aber im Modell und in der Datenmenge zu suchen, nicht in der Hardware.
2 — (b) aus dem ausgegebenen Tourenplan. Eine Prüfung muss von der Lösung ausgehen und die Anforderungen unabhängig nachrechnen. (a) fragt die Ladungsdimension — also ausgerechnet den Baustein, dessen Fehlen der Fehler war (siehe Abschnitt 8.7); existiert sie nicht, stürzt die Prüfung ab, existiert sie, kann sie per Konstruktion nie verletzt sein. (c) prüft gar nichts: Ob eine Kapazität in der Zielfunktion bepreist ist, sagt nichts darüber, ob sie eingehalten wurde.
-
3 — (c) hierarchisch lockern.INFEASIBLE sagt nur, dass ein Widerspruch existiert, nicht welche Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der Kostenzerlegung. Genau das tut Infeasibility_Diagnose.py. (a) hilft nicht: INFEASIBLE ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
+
3 — (c) hierarchisch lockern.INFEASIBLE sagt nur, dass ein Widerspruch existiert, nicht welche Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der Kostenzerlegung. Genau das tut Infeasibility_Diagnose.py. (a) hilft nicht: INFEASIBLE ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
Selbsttest
Durch hierarchische Relaxation: Schlupfvariablen mit hohen, gestaffelten Strafkosten für die verletzbaren Bedingungen.
Die erste wird getötet (test_pruefung_findet_kapazitaetsverletzung). Die zweite ist interessanter: Sie macht die Prüfung strenger statt schwächer — eine Lösung mit einer Menge von exakt 0 würde als negativ beanstandet. Ob sie überlebt, hängt daran, ob die Suite eine Instanz enthält, in der ein Produkt mit Menge 0 vorkommt. In der Schreinerei ist das nicht der Fall — die Mutation überlebt also und zeigt eine echte Lücke: Es fehlt eine Testinstanz, in der ein Produkt nicht produziert wird.
23.4 — Der Benchmark mit MILP. Zu erwarten ist, dass sich die Reihenfolge ändert. Beim reinen LP entscheidet vor allem der Modellaufbau in Python; beim MILP verschiebt sich das Gewicht zum Lösen, und dort spielen die Branch-and-Bound-Heuristiken der Bibliotheken gegeneinander. Die Spalte „Anteil” sollte bei allen deutlich fallen — nicht weil der Aufbau schneller würde, sondern weil das Lösen langsamer wird. Genau deshalb steht im Kapitel, dass die Tabelle nichts über MILPs sagt.
-
23.5 — Der Dienst mit Zeitlimit. Der Modellbauer in or_kern.py nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei GLOP solver.SetTimeLimit(millisekunden)). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver ZEITLIMIT, ist stand weiterhin gescheitert, aber mit einer anderen Begründung als bei UNZULAESSIG — der Unterschied zwischen „rechne länger” und „ändere das Modell” (Kapitel 6).
+
23.5 — Der Dienst mit Zeitlimit. Der Modellbauer in or_kern.py nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei GLOPsolver.SetTimeLimit(millisekunden)). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver ZEITLIMIT, ist stand weiterhin gescheitert, aber mit einer anderen Begründung als bei UNZULAESSIG — der Unterschied zwischen „rechne länger” und „ändere das Modell” (Kapitel 6).
Für den Test braucht es eine Instanz, die das Limit reißt. Ein LP eignet sich schlecht dafür; nehmen Sie ein MILP mit einigen hundert Binärvariablen und ein Limit von 0,1 Sekunden.
Finde den Denkfehler — „Die Suite ist grün, das Modell stimmt”
„Grün” heißt nur, dass kein Test fehlgeschlagen ist — das gilt auch für eine Suite aus lauter assert True. „Prüft etwas” heißt, dass die Tests bei einem eingebauten Fehler rot würden; genau das misst der Mutationstest.
Weil sonst nicht erkennbar ist, welche Hälfte die Zeit kostet. Im Kapitel gehen bei OR-Tools 78 % in den Aufbau — wer nur die Gesamtzeit sieht, wechselt den Solver und ändert damit fast nichts.
Weil die Rechnung Sekunden bis Minuten dauert. Eine synchrone Antwort läuft in den Timeout des Reverse Proxy und blockiert währenddessen einen Arbeiter. 202 heißt „angenommen, noch nicht fertig” — genau die richtige Aussage.
-
Ein Threadpool genügt, wenn der Solver den GIL freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während Solve(), im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
+
Ein Threadpool genügt, wenn der Solver den GIL freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während Solve(), im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
modell.Add(x <= M * y) # CP-SAT# LP/MILP: Zeile x - M*y <= 0
-
⚠️ M so klein wie möglich — idealerweise die ohnehin vorhandene Kapazitätsgrenze von x. Zu großes M macht die LP-Relaxation wertlos (Abschnitt 6.5).
+
⚠️ M so klein wie möglich — idealerweise die ohnehin vorhandene Kapazitätsgrenze von x. Zu großes M macht die LP-Relaxation wertlos (Abschnitt 6.5).
B2 — Semikontinuierliche Variable
Regel. „Entweder gar nicht oder mindestens L (und höchstens U).“
@@ -400,19 +400,19 @@
⚠️ Die häufigste Verwechslung:L\,y_t \le x_tje Periode (B2) ist ein anderes Modell — es verlangt in jeder einzelnen Periode eine Mindestmenge und ist erheblich strenger. Wer den Jahresvertrag so formuliert, erzeugt ein unlösbares Modell und sucht den Fehler dann in den Daten.
-
⚠️ Die obere Kopplung nicht vergessen. Ohne \sum_t x_t \le U\,y kann das Modell y = 0 setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der Trickle Flow aus Abschnitt 6.5, nur andersherum. U ist die Jahreskapazität des Lieferanten, keine runde Zahl.
+
⚠️ Die obere Kopplung nicht vergessen. Ohne \sum_t x_t \le U\,y kann das Modell y = 0 setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der Trickle Flow aus Abschnitt 6.5, nur andersherum. U ist die Jahreskapazität des Lieferanten, keine runde Zahl.
B27 — Budgetlimit
Regel. „Alle Maßnahmen zusammen dürfen 2 Mio. € nicht überschreiten.“
\sum_i c_i x_i \le B
-
Mit x_i \in \{0,1\} ist das ein Rucksackproblem — dasselbe Muster, das Kapitel 6 an Rucksack.py vorrechnet und Kapitel 10 als Pricing-Teilproblem wiederverwendet.
+
Mit x_i \in \{0,1\} ist das ein Rucksackproblem — dasselbe Muster, das Kapitel 6 an Rucksack.py vorrechnet und Kapitel 10 als Pricing-Teilproblem wiederverwendet.
modell.Add(sum(KOSTEN[i] * x[i] for i in massnahmen) <= BUDGET)# Mehrere Toepfe: je Topf eine Zeile - NICHT die Summe ueber alle Toepfefor topf, grenze in BUDGETS.items(): modell.Add(sum(KOSTEN[i] * x[i] for i in massnahmenif TOPF[i] == topf) <= grenze)
-
💡 Der Schattenpreis des Budgets ist die Zahl, nach der die Geschäftsführung fragt: „Was bringt der nächste Euro?“ Bei einem LP ist er direkt ablesbar. Bei Ganzzahligkeit gibt es ihn nicht — die Dualwerte der Relaxation sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (Abschnitt 5.9).
+
💡 Der Schattenpreis des Budgets ist die Zahl, nach der die Geschäftsführung fragt: „Was bringt der nächste Euro?“ Bei einem LP ist er direkt ablesbar. Bei Ganzzahligkeit gibt es ihn nicht — die Dualwerte der Relaxation sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (Abschnitt 5.9).
⚠️ Die LP-Relaxation verspricht zu viel. Sie darf die letzte Maßnahme anteilig kaufen und liefert deshalb eine Schranke, die spürbar über dem tatsächlich Erreichbaren liegen kann. Wer sie als Prognose berichtet, verspricht Geld, das nicht kommt.
@@ -426,7 +426,7 @@
B14 — Nichtüberlappung
Regel. „Eine Maschine bearbeitet nur einen Job gleichzeitig.“
Die Strafe muss hoch genug sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber endlich, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
+
Die Strafe muss hoch genug sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber endlich, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
B19 — Hierarchische Ziele (lexikografisch)
Regel. „Erst die Besetzung sicherstellen, dann die Fairness optimieren.“
Variante A — Gewichtung: Strafen um Größenordnungen staffeln (10\,000 \gg 100 \gg 1). Einfach, aber bei extremen Skalenunterschieden numerisch heikel.
@@ -475,7 +475,7 @@
B20 — Symmetriebrechung
Regel. „Drei identische Maschinen — der Solver soll nicht alle Vertauschungen durchprobieren.“
Ohne Symmetriebrechung durchsucht Branch-and-Bound k! gleichwertige Lösungen. Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.
+
Ohne Symmetriebrechung durchsucht Branch-and-Boundk! gleichwertige Lösungen. Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.
B21 — Worst-Case-Abzug (robuste Formulierung)
Regel. „Rechne nicht mit dem geschätzten Wert, sondern mit dem ungünstigsten plausiblen.“
Bei Box-Unsicherheit \mu_i \in [\hat\mu_i - \delta_i, \hat\mu_i + \delta_i] und w \ge 0: \hat{\boldsymbol{\mu}}^\top\mathbf{w} \;\longrightarrow\; \hat{\boldsymbol{\mu}}^\top\mathbf{w} - \boldsymbol{\delta}^\top\mathbf{w}
💡 Ganzzahligkeit nicht fordern! Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist scipy.optimize.linear_sum_assignment (Ungarischer Algorithmus, O(n^3)) noch deutlich schneller.
+
💡 Ganzzahligkeit nicht fordern! Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist scipy.optimize.linear_sum_assignment (Ungarischer Algorithmus, O(n^3)) noch deutlich schneller.
B24 — Subtour-Eliminierung
MTZ (einfach, aber schwach):u_i - u_j + C x_{ij} \le C - d_j \qquad \forall i\ne j
-
Besser in der Praxis:AddCircuit in CP-SAT oder die Routing-Bibliothek von OR-Tools.
Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)
+
Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)
Was dieser Anhang nicht ist. Kein Vergleich und keine Empfehlung. Welche Bibliothek für welche Aufgabe taugt, steht in Abschnitt 3.6; denselben Fall in vier Bibliotheken nebeneinander zeigt Ein_System_Vier_Ansaetze.py, die beiden Modellierungssprachen Modellierungsschichten.py. Hier geht es allein ums Nachschlagen.
D1 — SciPy: linprog und milp
-
Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.
+
Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.
Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
+
Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
import numpy as npimport highspy
@@ -205,12 +205,12 @@
Die drei häufigsten Stolpersteine
Nicht zusammen mit ortools importieren. Beide bringen eine eigene HiGHS-Kopie mit; im selben Prozess endet das in undefined symbol (Anhang C, C9). Auch cvxpy zieht highspy bei der Solver-Erkennung mit hinein.
-
Das CSR-Format stimmt oder es stimmt still nicht.starts hat so viele Einträge wie Zeilen, indices und values so viele wie Nichtnullen. Ein falscher starts-Eintrag erzeugt ein anderes, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
+
Das CSR-Format stimmt oder es stimmt still nicht.starts hat so viele Einträge wie Zeilen, indices und values so viele wie Nichtnullen. Ein falscher starts-Eintrag erzeugt ein anderes, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
output_flag abschalten, sonst überschwemmt das Solverprotokoll jede Ausgabe.
D3 — OR-Tools: pywraplp
-
Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht: Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
+
Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht:Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
from ortools.linear_solver import pywraplp# "GLOP" = LP, "SCIP" oder "CBC" = MILP, "SAT" = CP-SAT als MILP-Backend
@@ -290,7 +290,7 @@
D5 — CVXPY
-
Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
+
Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
import numpy as npimport cvxpy as cp
@@ -319,13 +319,13 @@
print("Solverfehler:", problem.status)
Die drei häufigsten Stolpersteine
-
DCP-Regeln vor dem Lösen prüfen.problem.is_dcp() und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen DCPError ohne Ort zu werfen (Anhang C, C8).
+
DCP-Regeln vor dem Lösen prüfen.problem.is_dcp() und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen DCPError ohne Ort zu werfen (Anhang C, C8).
OPTIMAL_INACCURATE ist kein Erfolg. Der Solver hat aufgegeben und meldet das leise. Diesen Fall immer eigens behandeln.
Der Aufbau kann teurer sein als das Lösen. Schleifen über Szenarien durch Vektorausdrücke ersetzen; Parameter statt Neuaufbau, wenn sich nur Zahlen ändern.
D6 — Modellierungssprachen: Pyomo und Linopy
-
Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy denkt in beschrifteten Arrays und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. Wofür nicht: ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
+
Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy denkt in beschrifteten Arrays und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. Wofür nicht: ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
Die Finanzkapitel ab Kapitel 18 sind eine Vertiefungsdomäne, kein Pflichtstoff. Wer beruflich mit Logistik, Produktion oder Energie zu tun hat, liest die ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas fehlt — die Methoden sind dieselben.
-
Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen: Schätzfehler in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
+
Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:Schätzfehler in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
Ein Hinweis vorweg: Die Kapitel ab Kapitel 18 laden aktuelle Kursdaten über yfinance und brauchen dafür einen Internetzugang. Weil die Daten sich täglich ändern, sind die abgedruckten Zahlen dort Beispielläufe, keine reproduzierbaren Werte — anders als im übrigen Buch. Das Brückenkapitel kommt ohne Internet aus.
@@ -121,7 +121,7 @@
📌 Kapitel auf einen Blick
Worum geht es? Um den Nachweis, dass die Verfahren aus Teil II und III unverändert in der Finanzwelt funktionieren — und um die drei Stellen, an denen die Analogie endet.
Danach können Sie: ein betriebswirtschaftliches Modell in die andere Domäne übersetzen, ohne den Modellcode anzufassen; Schattenpreise in beiden Welten lesen; und benennen, welche Annahmen dabei nicht mitwandern.
Zeitbedarf: ca. 2,5 Stunden.
Programme:
@@ -195,18 +195,18 @@
Und jetzt der Punkt. Die Klasse heißt Produktionsproblem, und das Depot passt hinein, ohne dass eine Zeile geändert wurde. Kein Adapter, keine Unterklasse, keine Fallunterscheidung — die Struktur ist dieselbe, weil das Problem dasselbe ist: knappe Größen auf konkurrierende Verwendungen verteilen.
-
Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. 60,26 € je Einheit Risikobudget — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der Preis des Risikos. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis aus Kapitel 5.
+
Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. 60,26 € je Einheit Risikobudget — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der Preis des Risikos. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis aus Kapitel 5.
🎯 Merksatz Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei Datensätze. Ob das der Fall ist, erkennt man daran, dass derselbe Code ohne Änderung läuft — nicht daran, dass eine Tabelle es behauptet.
-
Warum funktioniert das? Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
+
Warum funktioniert das? Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
16.2 Lernziele
Nach diesem Kapitel können Sie …
… ein Allokationsproblem aus der einen Domäne in die andere übersetzen, indem Sie die Daten austauschen statt des Codes.
… den Schattenpreis in beiden Welten deuten — als Wert einer Ressource und als Preis des Risikos.
-
… erklären, warum die CVaR-Formulierung aus Kapitel 20 auf Lieferverzüge genauso passt wie auf Kursverluste.
+
… erklären, warum die CVaR-Formulierung aus Kapitel 20 auf Lieferverzüge genauso passt wie auf Kursverluste.
… die drei Stellen benennen, an denen die Analogie nicht trägt — und begründen, warum das Übertragen von Methoden dort gefährlich wird.
Aufgabe 16.1 ⭐ — Die dritte Ressource. Ergänzen Sie im Depot eine dritte knappe Größe: „Liquidität” (wie schnell sich eine Anlage verkaufen lässt). Was entspricht ihr in der Werkstatt?
Aufgabe 16.2 ⭐ — Den Schattenpreis lesen. Das Risikobudget hat einen Schattenpreis von 60,26 €. Die Aufsicht bietet an, es gegen eine Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — und welche Einschränkung dieser Aussage kennen Sie aus Abschnitt 5.9?
-
Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts. Übertragen Sie die Ledoit-Wolf-Shrinkage aus Kapitel 18 auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
+
Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts. Übertragen Sie die Ledoit-Wolf-Shrinkage aus Kapitel 18 auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
Aufgabe 16.4 ⭐⭐ — CVaR mit Ganzzahligkeit. Der Einkauf darf höchstens drei Lieferanten beauftragen. Ergänzen Sie die entsprechende Kardinalitätsbedingung (Kapitel 6, Muster 5) und vergleichen Sie den CVaR mit dem teilbaren Fall. Was kostet die Ganzzahligkeit?
Aufgabe 16.5 ⭐⭐⭐ — Die eigene Brücke. Nehmen Sie ein Modell aus Ihrem Arbeitsumfeld und suchen Sie die Entsprechung in der jeweils anderen Welt. Prüfen Sie mit der Tabelle aus Abschnitt 16.5, ob die Übertragung trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
Eine Logistikerin liest dieses Kapitel und überträgt es zurück. Ihre Aufgabe: Aus 40 möglichen Lieferanten sollen die besten ausgewählt und Bestellmengen zugeteilt werden. Sie geht vor wie im Portfoliomanagement:
-
„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio.”
+
„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio.”
Das Vorgehen ist methodisch sauber, die Rechnung stimmt, und das Ergebnis sieht plausibel aus.
Zwei Dinge daran sind falsch — und das eine ist genau der Fehler, vor dem dieses Kapitel warnt, das andere ein zusätzlicher, den es nicht behandelt. Welche?
Worum geht es? Um ein völlig anderes Denkmodell: Statt algebraischer Ungleichungen arbeitet Constraint Programming mit Wertebereichen und logischen Regeln. Für Dienstpläne, Zuordnungen und Reihenfolgen ist das die mit Abstand produktivste Methode.
Danach können Sie: Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und alle fünf Solver-Antworten auseinanderhalten, statt nur auf OPTIMAL zu prüfen.
+
Danach können Sie: Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und alle fünf Solver-Antworten auseinanderhalten, statt nur auf OPTIMAL zu prüfen.
Zeitbedarf: ca. 6,5 Stunden.
Programme: Propagation_Demo.py
@@ -151,7 +151,7 @@ Do -> Bux
Fr -> Abel
-
Elf Zeilen Modell, und der Plan steht. Beachten Sie besonders die Zeile m.AddExactlyOne(...). Sie sagt wörtlich, was gemeint ist: genau eine Person je Tag. In MILP hätte man dafür eine Gleichung \sum_p x_{p,d} = 1 aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in CP-SAT bleibt er eine Zeile.
+
Elf Zeilen Modell, und der Plan steht. Beachten Sie besonders die Zeile m.AddExactlyOne(...). Sie sagt wörtlich, was gemeint ist: genau eine Person je Tag. In MILP hätte man dafür eine Gleichung \sum_p x_{p,d} = 1 aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in CP-SAT bleibt er eine Zeile.
Und jetzt sehen Sie sich das Ergebnis genau an.Cor bekommt keinen einzigen Dienst. Dane arbeitet Montag und Dienstag hintereinander. Ist das ein Fehler?
Nein — und das ist die wichtigste Lektion dieses Kapitels. Der Plan erfüllt jede Regel, die wir aufgeschrieben haben. Wir haben nur nie nach Fairness gefragt, und wir haben nie gesagt, dass Dienste nicht aufeinanderfolgen sollen. Ein Solver liefert exakt das Bestellte — nicht das Gemeinte.
@@ -162,17 +162,17 @@ Fr -> Abel
Nach diesem Kapitel können Sie …
… erklären, wodurch sich Constraint Programming von MILP unterscheidet.
-
… die Wirkung von Constraint Propagation an einem Beispiel nachvollziehen.
… die wichtigsten globalen Constraints einsetzen: AddExactlyOne, AddAllDifferent, AddNoOverlap, AddCumulative.
… mit Intervallvariablen ein Maschinenbelegungsproblem modellieren.
… harte Regeln und weiche Ziele in einem Modell kombinieren und gewichten.
… die fünf CP-SAT-Statusfälle unterscheiden — insbesondere INFEASIBLE (es gibt keine Lösung) von UNKNOWN (es wurde keine gefunden) und MODEL_INVALID (Ihr Code ist fehlerhaft).
… begründen, warum num_workers = 1 für einen reproduzierbaren Lauf nötig ist und ein fester random_seed allein nicht genügt — und was ein Test deshalb prüfen darf.
-
… begründen, warum ein Strafgewicht ein Wechselkurs ist, und welche Regeln deshalb niemals in die Zielfunktion gehören.
+
… begründen, warum ein Strafgewicht ein Wechselkurs ist, und welche Regeln deshalb niemals in die Zielfunktion gehören.
7.3 Ein anderes Denkmodell
-
Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:
+
Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:
Der CP-SAT-Solver von Google OR-Tools verbindet Constraint Programming mit modernen SAT-Techniken (Boolean Satisfiability, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere CDCL (Conflict-Driven Clause Learning, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und Job-Shop-Scheduling.
+
Der CP-SAT-Solver von Google OR-Tools verbindet Constraint Programming mit modernen SAT-Techniken (Boolean Satisfiability, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere CDCL (Conflict-Driven Clause Learning, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und Job-Shop-Scheduling.
🔤 Formel-Übersetzer: was ein globales Constraint zusichert
-
Globale Constraints tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
+
Globale Constraints tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
@@ -424,7 +424,7 @@ Fr -> Abel
-
💡 Warum globale Constraints so viel bringenAddAllDifferent über 9 Variablen ersetzt \binom{9}{2} = 36 paarweise Ungleichheitsbedingungen — und propagiert stärker als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten Graphen). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
+
💡 Warum globale Constraints so viel bringenAddAllDifferent über 9 Variablen ersetzt \binom{9}{2} = 36 paarweise Ungleichheitsbedingungen — und propagiert stärker als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten Graphen). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
koppeln die Hilfsvariable lochin beide Richtungen an das Muster „arbeitet–frei–arbeitet“. Die erste Zeile erzwingt: wennloch = 1, dann liegt das Muster vor. Die zweite: wennloch = 0, dann liegt es nicht vor. In diesem Modell genügte streng genommen die erste Zeile, weil loch nur mit positiven Strafkosten vorkommt und der Minimierer es daher von selbst auf 0 drückt — die zweite Zeile macht das Modell aber robust gegen spätere Änderungen und ist selbstdokumentierend.
2. Fairness über Spannweite (W3).AddMaxEquality und AddMinEquality sind CP-SAT-Konstrukte, die in MILP mehrere Big-M-Ungleichungen bräuchten. Die Spannweite \max - \min zu minimieren ist ein Standardrezept für „gleichmäßig verteilen“.
-
3. Kostenzerlegung. Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: „Warum bekomme ich die Stunde?“ Wer darauf keine Antwort hat, dessen System wird nicht benutzt (Kapitel 22, Explainable OR).
+
3. Kostenzerlegung. Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: „Warum bekomme ich die Stunde?“ Wer darauf keine Antwort hat, dessen System wird nicht benutzt (Kapitel 22, Explainable OR).
4. Statusauswertung ohne CP-SAT-Vokabular. Nach Solve() steht nur eine einzige Zeile mit einer CP-SAT-Konstante darin:
status = status_von_cpsat(rohstatus)
-
Danach heißen die Fälle status.modellfehler (die Regeln widersprechen sich — das Modell muss geändert werden) und status.brauchbar (es liegt etwas Ausführbares vor). Das ist dieselbe Fallunterscheidung wie bei HiGHS in Kapitel 6 und bei GLOP in Kapitel 5; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen cp_model.INFEASIBLE schreibt, kann ihn bei einem Solverwechsel wegwerfen — Abschnitt 22.7 führt genau diesen Wechsel vor.
+
Danach heißen die Fälle status.modellfehler (die Regeln widersprechen sich — das Modell muss geändert werden) und status.brauchbar (es liegt etwas Ausführbares vor). Das ist dieselbe Fallunterscheidung wie bei HiGHS in Kapitel 6 und bei GLOP in Kapitel 5; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen cp_model.INFEASIBLE schreibt, kann ihn bei einem Solverwechsel wegwerfen — Abschnitt 22.7 führt genau diesen Wechsel vor.
Die Königsdisziplin von CP-SAT sind Zeitplanungsprobleme. Intervallvariablen sind dabei das zentrale Werkzeug.
-
Problem. Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (Makespan).
+
Problem. Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (Makespan).
#!/usr/bin/env python3# JobShop_Intervalle.py
@@ -1154,12 +1154,12 @@ random_seed = 1 in JEDEM Lauf, 4 Wiederholungen je Arbeiterzahl.
deshalb eine Entscheidung fuer den Test, nicht fuer den Betrieb.
==============================================================================
Was die Messung zeigt
-
Erstens: Die Beschleunigung ist überlinear. Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern gut zwölfmal schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt nicht achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern verschiedene Strategien nebeneinander — Suche mit LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
+
Erstens: Die Beschleunigung ist überlinear. Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern gut zwölfmal schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt nicht achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern verschiedene Strategien nebeneinander — Suche mit LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
Daraus folgt zweierlei. Ein Speedup über der Kernzahl ist bei CP-SAT normal und kein Grund zum Misstrauen. Und der Zugewinn flacht ab: Zwischen vier und acht Arbeitern lag in unseren Wiederholungen mal das eine, mal das andere vorn — ab einer gewissen Zahl kosten Abstimmung und Speicherbandbreite mehr, als ein weiterer Suchstrang einbringt. Auch hier gilt: messen, nicht schätzen.
Zweitens — und das ist der Befund, der die Testpraxis bestimmt: Der Seed genügt nicht. Bei einem Arbeiter liefern vier Läufe einen Plan. Schon bei zwei Arbeitern liefern vier Läufe drei verschiedene — bei identischem random_seed und identischem Zielwert 183.
Es braucht also keine acht Arbeiter, um die Reproduzierbarkeit zu verlieren. Zwei genügen. Der Grund ist einfach, sobald man ihn ausspricht: Der Seed legt fest, wie ein einzelner Suchstrang würfelt. Er legt nicht fest, welcher von mehreren zuerst fertig wird — das entscheidet die Uhr, und die ist bei jedem Lauf anders.
-
⚠️ random_seed allein macht nichts reproduzierbar Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen parallelen Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln. Reproduzierbar wird der Lauf erst mit num_workers = 1und Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
+
⚠️ random_seed allein macht nichts reproduzierbar Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen parallelen Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln. Reproduzierbar wird der Lauf erst mit num_workers = 1und Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
Was daraus für Tests folgt
Der Zielwert war in allen sechzehn Läufen derselbe: 183. Die Gestalt der Lösung war es nicht. Genau daran entscheidet sich, was ein Test prüfen darf:
@@ -1576,7 +1576,7 @@ genau die Information, die zur Fehlersuche noetig waere.
Aufgabe 7.1 ⭐ — Propagation nachvollziehen.x_1 \in \{1,\dots,6\}, x_2 \in \{1,\dots,6\}, Bedingungen: x_1 < x_2 und x_1 + x_2 = 8. Reduzieren Sie beide Wertebereiche von Hand so weit wie möglich.
Aufgabe 7.2 ⭐ — Hart oder weich? Klassifizieren Sie und begründen Sie, welche Strafhöhe Sie ansetzen würden: (a) Gesetzliche Mindestruhezeit. (b) „Herr Bauer möchte montags frei.“ (c) „In jeder Schicht mindestens eine examinierte Kraft.“ (d) „Möglichst keine geteilten Dienste.“ (e) „Kein Dienst länger als 10 Stunden.“
Aufgabe 7.3 ⭐⭐ — Regeln ergänzen. Erweitern Sie das Vertretungssystem um: (a) Frau Albrecht ist ab Slot 3 nicht mehr verfügbar. (b) Herr Bauer und Frau Koch sollen nicht beide am selben Tag eingesetzt werden. (c) Wer Slot 1 übernimmt, soll bevorzugt auch Slot 2 bekommen (Belohnung statt Strafe).
-
Aufgabe 7.4 ⭐⭐ — Infeasibility erzeugen und diagnostizieren. Setzen Sie MAX_VERTRETUNGEN = 0. Was meldet das Programm? Bauen Sie anschließend eine Schlupfvariable je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
+
Aufgabe 7.4 ⭐⭐ — Infeasibility erzeugen und diagnostizieren. Setzen Sie MAX_VERTRETUNGEN = 0. Was meldet das Programm? Bauen Sie anschließend eine Schlupfvariable je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
Aufgabe 7.5 ⭐⭐ — Sudoku. Lösen Sie ein 9×9-Sudoku mit CP-SAT. Sie brauchen nur NewIntVar(1, 9, ...) und AddAllDifferent für Zeilen, Spalten und die neun 3×3-Blöcke. Wie viele Zeilen Modellcode benötigen Sie?
Aufgabe 7.6 ⭐⭐ — Die Grenze der Parallelität finden.Parallele_Suche.py misst 1, 2, 4 und 8 Arbeiter. (a) Ergänzen Sie 16 und 24 (bzw. die Kernzahl Ihres Rechners). Ab wo bringt ein weiterer Arbeiter nichts mehr — und wird es irgendwann wieder schlechter? (b) Setzen Sie random_seed bei jedem Lauf auf einen anderen Wert, aber num_workers = 1. Wie viele verschiedene Pläne kommen jetzt heraus? Was folgt daraus für die Frage, welcher der beiden Parameter die Reproduzierbarkeit sichert? (c) Vergrößern Sie die Instanz auf 15 Aufträge. Ab welcher Größe erreicht der Lauf mit einem Arbeiter das Zeitlimit, während acht noch OPTIMAL melden?
Aufgabe 7.7 ⭐⭐⭐ — Job-Shop erweitern. Ergänzen Sie JobShop_Intervalle.py um: (a) Rüstzeiten von 1 Zeiteinheit zwischen zwei Aufträgen auf derselben Maschine. (b) Fälligkeitstermine je Auftrag mit Strafkosten für Verspätung. (c) Eine zweite Fräse (Kapazität 2 statt 1) mit AddCumulative.
@@ -1777,7 +1777,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
Gewichte nach Bauchgefühl setzen. Rechnen Sie stattdessen die Tabelle wie oben und lassen Sie den Auftraggeber die Zeile aussuchen. Das dauert eine Stunde und ersetzt drei Abstimmungsrunden.
Eine unverhandelbare Regel weich modellieren. Alles, was einen Preis hat, wird irgendwann gekauft. Gesetzliche Ruhezeiten, Qualifikationsanforderungen und Betriebsvereinbarungen gehören unter die harten Nebenbedingungen — auch wenn das Modell dann INFEASIBLE melden kann. Dieses INFEASIBLE ist die ehrliche Antwort: Der Plan ist mit dem vorhandenen Personal nicht zulässig zu machen.
-
Gewichte in verschiedenen Einheiten mischen. Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren Konditionszahl obendrein leidet (Abschnitt 2.7).
+
Gewichte in verschiedenen Einheiten mischen. Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren Konditionszahl obendrein leidet (Abschnitt 2.7).
Nur ein Gewicht ausprobieren. Ein einziger Lauf sagt nichts darüber, ob Sie nahe an einem Kipppunkt stehen. Zwischen den Gewichten 5 und 10 ändert sich der Plan oben erheblich.
@@ -1810,7 +1810,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
7.13 Zusammenfassung
CP denkt in Wertebereichen und Regeln, nicht in Ungleichungen und Schranken.
-
Propagation streicht unmögliche Werte, bevor gesucht wird; Conflict Learning verhindert, dieselbe Sackgasse zweimal zu betreten.
+
Propagation streicht unmögliche Werte, bevor gesucht wird; Conflict Learning verhindert, dieselbe Sackgasse zweimal zu betreten.
Globale Constraints wie , und ersetzen ganze Blöcke von Big-M-Ungleichungen und propagieren stärker.
Intervallvariablen sind das Werkzeug für Zeitplanung; AddNoOverlap erledigt in einer Zeile, was in MILP dutzende Disjunktionen kostet.
Weiche Ziele mit Strafkosten machen Modelle robust gegen INFEASIBLE — und die Kostenzerlegung macht sie erklärbar.
Worum geht es? Um zwei Schwächen des Markowitz-Modells: Es unterschätzt Extremverluste und ignoriert die Kosten des Umschichtens. Beide lassen sich mit konvexen Mitteln beheben.
Danach können Sie: VaR und CVaR unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die L_1-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
Danach können Sie:VaR und CVaR unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die L_1-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
… Einheiten konsistent halten und Nebenbedingungen vektorisieren.
… erklären, warum zwei Anlagen mit identischem VaR völlig verschiedene Extremverluste haben können.
-
… die Folgen fehlender Subadditivität für die Verteilung von Risikobudgets benennen.
+
… die Folgen fehlender Subadditivität für die Verteilung von Risikobudgets benennen.
20.3 Die zwei Schwächen des Markowitz-Modells
@@ -319,7 +319,7 @@ VaR: Portfolio 98.00 vs. Summe -4.00 -> VERLETZT die Subadditivitaet!
CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
========================================================================================
Die 6-Sigma-Zeile setzt die Sache ins Verhältnis: Unter Normalverteilung wäre ein solcher Tag ein Ereignis von einmal in zwei Millionen Jahren. Unter der t-Verteilung mit drei Freiheitsgraden — die realen Aktienrenditen deutlich näher kommt — passiert er etwa alle zwei Jahre. Der Faktor beträgt fast eine Million.
-
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen Gewinn von 2 €, im Portfolio einen Verlust von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung senkt das Risiko, wie es sein muss.
+
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen Gewinn von 2 €, im Portfolio einen Verlust von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung senkt das Risiko, wie es sein muss.
Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie VaR_CVaR_Demo.py. Erzeugt von bilder_04/erzeuge_var_vs_cvar.py.
@@ -336,7 +336,7 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py.
Value at Risk (\text{VaR}_\alpha): Der Verlust, der mit Wahrscheinlichkeit \alpha nicht überschritten wird.
-
Conditional Value at Risk (\text{CVaR}_\alpha, auch Expected Shortfall): Der durchschnittliche Verlust in den schlimmsten (1-\alpha) Prozent der Fälle.
+
Conditional Value at Risk (\text{CVaR}_\alpha, auch Expected Shortfall): Der durchschnittliche Verlust in den schlimmsten (1-\alpha) Prozent der Fälle.
📐 Der Unterschied in einem Satz Der VaR sagt: „In 95 % der Tage verlieren Sie höchstens 1,86 %.“ Der CVaR sagt: „Und wenn es doch schiefgeht, verlieren Sie im Mittel 2,99 %.“
Beide Zahlen stammen aus der Verteilung oben — nachzurechnen mit bilder_04/erzeuge_var_cvar.py.
📐 Formel-Lesehilfe — der Trick in drei Schritten * \gamma ist eine Hilfsvariable, die im Optimum automatisch den VaR annimmt. Man muss ihn also nicht vorher kennen — das ist der eigentliche Durchbruch. * -\mathbf{R}_s^\top\mathbf{w} ist der Verlust im Szenario s (Rendite mit negativem Vorzeichen). * u_s \ge \text{Verlust}_s - \gamma zusammen mit u_s \ge 0 bedeutet: u_s = \max(\text{Verlust}_s - \gamma,\ 0) — der Überschuss über die Schwelle. Liegt der Verlust unter \gamma, ist u_s = 0 und das Szenario zählt nicht.
Ohne Formel gesagt: „Wähle eine Schwelle \gamma. Zähle für jedes Szenario, wie weit der Verlust darüber hinausgeht. Der CVaR ist die Schwelle plus der gemittelte Überschuss — und zwar für diejenige Schwelle, bei der diese Summe minimal wird.“
-
Warum u_s automatisch das Maximum wird: Die Zielfunktion minimiert die Summe der u_s. Jedes u_s wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
+
Warum u_s automatisch das Maximum wird: Die Zielfunktion minimiert die Summe der u_s. Jedes u_s wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
In einem Satz:Der CVaR ist der Mittelwert der schlimmsten 1-\alpha Prozent — der Bruch vor der Summe sorgt allein dafür, dass durch die richtige Anzahl geteilt wird.
-
⚠️ Die teuerste Verwechslung des Kapitels > \alpha und 1-\alpha zu vertauschen führt zu keiner Fehlermeldung. Das Modell > rechnet weiter, nur eben über die falsche Menge. An 2 000 simulierten Tagesrenditen mit > Fat Tails gemessen: > > | Rechnung | gemittelt über | CVaR | > | — | — | — | > | richtig (\alpha = 0{,}95, Rand 5 %) | 100 von 2 000 Szenarien | 3,43 % | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | 0,18 % | > > Ein Faktor 19 — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > Die Gegenprobe kostet eine Zeile: Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt \alpha nicht.
+
⚠️ Die teuerste Verwechslung des Kapitels > \alpha und 1-\alpha zu vertauschen führt zu keiner Fehlermeldung. Das Modell > rechnet weiter, nur eben über die falsche Menge. An 2 000 simulierten Tagesrenditen mit > Fat Tails gemessen: > > | Rechnung | gemittelt über | CVaR | > | — | — | — | > | richtig (\alpha = 0{,}95, Rand 5 %) | 100 von 2 000 Szenarien | 3,43 % | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | 0,18 % | > > Ein Faktor 19 — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > Die Gegenprobe kostet eine Zeile: Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt \alpha nicht.
Ohne Formel gesagt: „Wenn du 5 % von A verkaufst und 5 % von B kaufst, hast du 10 % Umschlag und zahlst darauf Gebühren.“
Warum L_1 und nicht L_2? Die L_1-Norm ist konvex (also optimierbar) und erzeugt zusätzlich dünn besetzte Änderungen: Sie bevorzugt wenige große Umschichtungen gegenüber vielen kleinen. Das entspricht genau dem, was man in der Praxis will — nicht 50 Kleinstorders mit je 3 € Mindestgebühr.
-
In CVXPY schreibt man einfach cp.norm1(w - w_alt); intern wird das in lineare Hilfsvariablen zerlegt.
+
In CVXPY schreibt man einfach cp.norm1(w - w_alt); intern wird das in lineare Hilfsvariablen zerlegt.
Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: lambda_risk = 1.5 bedeutet dann effektiv eine Risikoaversion von 1{,}5/252 \approx 0{,}006. Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
+
Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: lambda_risk = 1.5 bedeutet dann effektiv eine Risikoaversion von 1{,}5/252 \approx 0{,}006. Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
Dieses Programm rechnet deshalb durchgehend auf Tagesbasis und annualisiert erst in der Ausgabe.
for s inrange(S): bedingungen.append(u[s] >=-R[s] @ w - gamma) # S einzelne Bedingungen
-
Beide beschreiben dasselbe Modell. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem Backtest mit 60 Rebalancings (Kapitel 21) summiert sich das erheblich.
-
Die allgemeine Regel: Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der Solver rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
+
Beide beschreiben dasselbe Modell. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem Backtest mit 60 Rebalancings (Kapitel 21) summiert sich das erheblich.
+
Die allgemeine Regel: Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der Solver rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
⚠️ Die Standardabweichung ist in CVXPY keine Wurzel
Im Vergleichsmodell optimiere_varianz() steht die Standardabweichung als
und nicht als das Naheliegende, cp.sqrt(cp.quad_form(w, sigma)). Der naheliegende Ausdruck ist nicht DCP und wird von CVXPY mit einem DCPError abgelehnt: cp.sqrt ist konkav und verlangt deshalb ein konkaves Argument, quad_form ist aber konvex. Die Regel wird verletzt, obwohl die Funktion als ganze mathematisch völlig harmlos ist — \sqrt{w^\top \Sigma w} ist konvex, DCP kann es nur nicht sehen.
+
und nicht als das Naheliegende, cp.sqrt(cp.quad_form(w, sigma)). Der naheliegende Ausdruck ist nicht DCP und wird von CVXPY mit einem DCPError abgelehnt: cp.sqrt ist konkav und verlangt deshalb ein konkaves Argument, quad_form ist aber konvex. Die Regel wird verletzt, obwohl die Funktion als ganze mathematisch völlig harmlos ist — \sqrt{w^\top \Sigma w} ist konvex, DCP kann es nur nicht sehen.
Wichtig ist die Fehlerdiagnose dahinter, weil sie leicht in die Irre geht: cp.psd_wrap(sigma) sieht nach der Lösung aus und ist keine. Es unterdrückt die numerische Beanstandung, dass \Sigma nicht als positiv semidefinit erkannt wird — gegen die strukturelle Regelverletzung im Aufbau hilft es nicht.
Der Ausweg ist immer derselbe: Zerlegen Sie \Sigma = LL^\top (Cholesky). Dann ist w^\top \Sigma w = \lVert L^\top w\rVert_2^2, und die Standardabweichung wird zur 2-Norm eines affinen Ausdrucks — konvex, regelkonform und für den Solver sogar die bessere Formulierung, weil sie direkt ein Kegelproblem ist. Der winzige Diagonalzuschlag 10^{-12} fängt den Fall ab, dass \Sigma numerisch nur halbdefinit ist (mehr Titel als Handelstage, duplizierte Spalten). Die vollständige Fehlertabelle steht in Anhang C.
-
💡 Zur Annualisierung von VaR und CVaR Ein häufiger Fehler skaliert den täglichen CVaR mit \sqrt{252}. Diese Wurzel-Zeit-Regel gilt streng nur für Standardabweichungen unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
+
💡 Zur Annualisierung von VaR und CVaR Ein häufiger Fehler skaliert den täglichen CVaR mit \sqrt{252}. Diese Wurzel-Zeit-Regel gilt streng nur für Standardabweichungen unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
Dieses Buch weist VaR und CVaR deshalb als Tageswerte aus und benennt die Skalierungsproblematik ausdrücklich. Wer Mehrtageshorizonte braucht, simuliert sie (Monte-Carlo aus Kapitel 12) statt zu skalieren.
Aufgabe 20.2 ⭐ — Warum kohärent? Erklären Sie in eigenen Worten, warum ein Risikomaß subadditiv sein sollte. Was bedeutet es wirtschaftlich, wenn diese Eigenschaft verletzt ist?
Aufgabe 20.3 ⭐⭐ — Rockafellar-Uryasev nachvollziehen. Zeigen Sie für die drei Szenarien mit Verlusten (1, 4, 9) und \alpha = 2/3: (a) Was ist der empirische CVaR? (b) Werten Sie \gamma + \frac{1}{S(1-\alpha)}\sum_s \max(\text{Verlust}_s-\gamma, 0) für \gamma \in \{0, 1, 4, 5, 9\} aus. (c) Bei welchem \gamma ist der Ausdruck minimal, und stimmt das Minimum mit (a) überein?
Aufgabe 20.4 ⭐⭐ — Einheiten prüfen. Angenommen, ein Modell verrechnet Jahresrendite gegen Tages-CVaR (siehe die Warnung in Abschnitt 20.6). Berechnen Sie: Welchem „effektiven“ täglichen \lambda entspräche lambda_risk = 1.5 dadurch? Welchen Wert müsste man stattdessen setzen, um dieselbe Wirkung wie RISIKOAVERSION = 1.5 im konsistenten Tagesmodell zu erzielen?
-
Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Variieren Sie RISIKOAVERSION von 0 bis 20 und tragen Sie Rendite, Volatilität, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
+
Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Variieren Sie RISIKOAVERSION von 0 bis 20 und tragen Sie Rendite, Volatilität, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
Aufgabe 20.6 ⭐⭐⭐ — Turnover-Grenze statt Strafe. Ersetzen Sie den Kostenterm durch eine harte Grenze\lVert\mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1 \le \tau und variieren Sie \tau \in \{0{,}05;\ 0{,}1;\ 0{,}25;\ 0{,}5;\ 1{,}0\}. (a) Wie verändert sich die erreichbare Rendite? (b) Was ist der Vorteil einer Grenze gegenüber einer Strafe — und was der Nachteil? (c) Wann würden Sie was einsetzen?
Worum geht es? Um die andere Antwort auf die Frage aus Kapitel 9: Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
+
Worum geht es? Um die andere Antwort auf die Frage aus Kapitel 9: Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
Voraussetzungen:Abschnitt 5.6 (Dualwerte) und Kapitel 6 (Rucksack.py). Beides wird gebraucht, aber nicht neu erklärt.
Danach können Sie: ein Problem in Master und Teilproblem zerlegen; die Spaltengenerierungsschleife selbst schreiben; und begründen, wann sich der Aufwand lohnt — die Antwort hängt an einer einzigen Kennzahl der Instanz.
Zeitbedarf: ca. 3 Stunden.
@@ -121,7 +121,7 @@
Die naheliegende Frage — „welches Stück kommt auf welche Rolle?“ — führt zu 35 Einzelentscheidungen. Die bessere Frage lautet: „Wie oft schneide ich welches Muster?“
Und jetzt der Punkt. Sehen Sie sich an, was da herauskommt: drei Schnittmuster. Kein Zuordnungsplan für 35 Stücke, sondern eine Anweisung, die an der Maschine hängen kann — neunmal dieses Muster, zweimal jenes, einmal das dritte.
Das ist kein kosmetischer Unterschied. Das Modell hat eine Variable je Muster statt einer je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschwindet ein Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.
-
🎯 Merksatz Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die Frage, worüber die Variablen laufen.
+
🎯 Merksatz Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die Frage, worüber die Variablen laufen.
Warum funktioniert das? Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei Breiten und einer 1 000-mm-Rolle sind es 16; bei dreizehn Breiten und 5 600 mm sind es zehntausende, und bei einer echten Papierfabrik mehr, als sich speichern lässt. Das ganze Kapitel handelt davon, wie man mit Mustern rechnet, ohne sie aufzuschreiben.
@@ -160,7 +160,7 @@
… erklären, warum das naheliegende Zuordnungsmodell an Symmetrie scheitert.
… ein Problem in Master und Pricing-Teilproblem zerlegen.
… das Abbruchkriterium über die reduzierten Kosten begründen.
… an einer Kennzahl der Instanz abschätzen, ob sich das Verfahren lohnt.
@@ -169,7 +169,7 @@
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable z_{jr} für „Stück j liegt auf Rolle r“, dazu y_r für „Rolle r wird benutzt”.
Zwei Dinge gehen dabei schief.
Die Größe. Bei 219 Zuschnitten und höchstens 82 Rollen sind das knapp 18 000 Binärvariablen — unangenehm, aber allein noch kein Hindernis.
-
Die Symmetrie. Das ist das eigentliche Problem. Alle Mutterrollen sind gleich. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht und arbeitet sie einzeln ab.
+
Die Symmetrie. Das ist das eigentliche Problem. Alle Mutterrollen sind gleich. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht und arbeitet sie einzeln ab.
⚠️ Woran man ein Symmetrieproblem erkennt
Der Suchbaum wächst, aber die Schranke bewegt sich nicht. Im Protokoll aus Abschnitt 6.8 sieht das so aus: Die Zahl der Knoten steigt in die Hunderttausende, der Incumbent verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
@@ -229,7 +229,7 @@
Fange mit wenigen Mustern an. Frage nach jeder Lösung, ob es ein noch nicht bekanntes Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.
-
Die Frage „lohnt sich noch ein Muster?” beantworten die Dualwerte des Master-LPs. Der Dualwert \pi_i zur Bedarfszeile i ist genau der Schattenpreis aus Abschnitt 5.6: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite i kostet.
+
Die Frage „lohnt sich noch ein Muster?” beantworten die Dualwerte des Master-LPs. Der Dualwert \pi_i zur Bedarfszeile i ist genau der Schattenpreis aus Abschnitt 5.6: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite i kostet.
Abb. 10.1: Der Kreislauf zwischen Master-LP und Pricing-Problem. Erzeugt von bilder_04/erzeuge_spaltengenerierung_flow.py.
@@ -277,7 +277,7 @@
Die letzte Zeile ist der Grund, warum das Verfahren exakt ist und nicht heuristisch: Wenn kein Muster mehr lohnende reduzierte Kosten hat, ist die LP-Lösung über allen Mustern optimal — auch über den nie erzeugten.
-
Und wie findet man das beste neue Muster? Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein Rucksackproblem:
+
Und wie findet man das beste neue Muster? Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein Rucksackproblem:
Worum geht es? Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.
+
Worum geht es? Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.
Voraussetzungen:Kapitel 12 (Erwartungswerte). Rekursion in Python.
-
Danach können Sie: Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
+
Danach können Sie: Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
🎯 Merksatz Eine Entscheidung ist nie für sich zu bewerten, sondern nur zusammen mit allem, was danach kommt. Genau das leistet die Wertfunktion: Sie fasst die gesamte Zukunft eines Zustands in einer einzigen Zahl zusammen. Und weil man die Zukunft kennen muss, bevor man die Gegenwart bewerten kann, rechnet dynamische Programmierung rückwärts.
-
Warum funktioniert das? Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt Bellmansches Optimalitätsprinzip und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
+
Warum funktioniert das? Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt Bellmansches Optimalitätsprinzip und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
📐 Formel-Lesehilfe * n_t — in Periode t verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * \eta („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * X_t — Restbestand nach dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * \lambda — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * \sigma — Volatilität je Periode.
+
📐 Formel-Lesehilfe * n_t — in Periode t verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * \eta („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * X_t — Restbestand nach dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * \lambda — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * \sigma — Volatilität je Periode.
Ohne Formel gesagt: „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet Nervenkitzel. Finde die Mitte.“
Die zwei Extremfälle: * \lambda \to 0 (risikoneutral): gleichmäßiges Aufteilen auf alle Perioden minimiert \sum n_t^2 bei fester Summe. * \lambda \to \infty (extrem risikoscheu): sofort alles verkaufen, um kein Risiko zu tragen.
Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie risk_aversion = 1e-6 würde dann in Wahrheit 0{,}1 bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
+
Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie risk_aversion = 1e-6 würde dann in Wahrheit 0{,}1 bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
Der Parameter lässt sich stattdessen herleiten. Beide Kostenterme werden in Euro gerechnet: * Marktauswirkung: \eta \cdot n_t^2 mit \eta in €/Stück². * Risiko: \tfrac{\lambda}{2}\cdot \sigma_{\text{Periode}}^2 \cdot P_0^2 \cdot X_t^2 — die Varianz des Werts der offenen Position, multipliziert mit der Risikoaversion.
Durch die Multiplikation mit P_0^2 (dem Quadrat des Aktienkurses) stimmen die Einheiten, und \lambda wird interpretierbar: Es ist der Preis, den man je Einheit Wertvarianz zu zahlen bereit ist.
@@ -819,7 +819,7 @@ Abweichung DP zur analytischen Loesung: 5.788 % (Diskretisierung: 1000 Stueck)
1e-03 | 97,000 ( 97.0 %) | fast alles sofort
====================================================================================
Die Lösung ist front-loaded: 41 % im ersten Schritt, dann fallend (25 %, 16 %, 10 %, 8 %). Das ist die typische Form — man baut Risiko früh ab, aber nicht abrupt. Die Sensitivitätstabelle zeigt, wie \lambda zwischen den beiden Extremen steuert: Bei \lambda = 10^{-7} verkauft das Modell gleichmäßig (Risiko ist egal), bei \lambda = 10^{-3} praktisch alles sofort (Risiko dominiert).
-
Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung 2 976 € oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
+
Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung 2 976 € oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
💻 Code-Durchgang: die analytische Gegenprobe und was ihre Abweichung bedeutet
Das Almgren-Chriss-Problem hat eine geschlossene Lösung: X_t = X_0\,\frac{\sinh(\kappa(T-t))}{\sinh(\kappa T)}, \qquad \kappa = \operatorname{arcosh}\!\Big(\tfrac{\tilde\kappa^2}{2}+1\Big),\qquad \tilde\kappa^2 = \frac{\lambda\,\sigma^2 P_0^2}{\eta}
Das ist der Fluch der Dimensionalität (Bellmans eigener Begriff): Jede zusätzliche Zustandsvariable multipliziert den Aufwand.
-
Gegenmittel: * Zustandsraumverkleinern: gröber diskretisieren, irrelevante Variablen weglassen. * Approximate Dynamic Programming:V_t durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * Reinforcement Learning: dieselbe Bellman-Gleichung, aber V wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * Nach geschlossenen Lösungen suchen — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.
+
Gegenmittel: * Zustandsraumverkleinern: gröber diskretisieren, irrelevante Variablen weglassen. * Approximate Dynamic Programming:V_t durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * Reinforcement Learning: dieselbe Bellman-Gleichung, aber V wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * Nach geschlossenen Lösungen suchen — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.
Aufgabe 13.1 ⭐ — Bausteine benennen. Ein Wanderer plant eine 5-Tages-Tour und muss täglich entscheiden, wie weit er läuft. Bestimmen Sie Stufe, Zustand, Aktion und Wertfunktion. Was gehört nicht in den Zustand?
Aufgabe 13.2 ⭐ — Optimalitätsprinzip anwenden. Warum folgt aus dem Optimalitätsprinzip, dass man rückwärts rechnen darf? Was würde schiefgehen, wenn die Kosten einer Periode auch von früheren Aktionen abhängen (und nicht nur vom aktuellen Zustand)?
Aufgabe 13.3 ⭐⭐ — Rückwärtsinduktion von Hand. 5 Einheiten in 3 Perioden, Kosten C(n) = n^2 + 2n. Erstellen Sie die vollständige Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit Bellman_Minimalbeispiel.py (angepasst).
-
Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP. Lösen Sie das Rucksackproblem aus Kapitel 6 mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.
+
Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP. Lösen Sie das Rucksackproblem aus Kapitel 6 mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.
Aufgabe 13.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Untersuchen Sie mit Mehrperiodige_Order_Execution.py: (a) Bei welchem \lambda verkauft das Modell in der ersten Periode mehr als 50 %? (b) Stellen Sie den Zusammenhang zwischen \lambda und den erwarteten Gesamtkosten dar. (c) Ein Händler sagt: „Ich will höchstens 20 % Marktauswirkungskosten und den Rest an Risiko.“ Welches \lambda setzen Sie?
Aufgabe 13.6 ⭐⭐⭐ — Zustandsraum erweitern. Erweitern Sie das Ausführungsmodell um einen zweiten Zustand: die aktuelle Orderbuchtiefe (3 Stufen: dünn/normal/tief), die mit gegebenen Übergangswahrscheinlichkeiten wechselt und \eta um Faktor 2 / 1 / 0,5 skaliert. Wie ändert sich die Strategie? Wie stark wächst die Rechenzeit?
Worum geht es? Was Operations Research ist, warum Ausprobieren ab einer gewissen Problemgröße hoffnungslos wird, und aus welchen vier Bausteinen jedes Optimierungsmodell besteht. Los geht es mit einem Problem, das Sie in fünf Minuten selbst lösen.
Voraussetzungen: Keine. Dies ist der Einstieg.
-
Danach können Sie: Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
+
Danach können Sie: Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
Zeitbedarf: ca. 3,5 Stunden inklusive Übungen.
Programme: Bot_Allokation.py
@@ -153,7 +153,7 @@
produkt = {"Tisch": (240, 3.0, 6.0), "Stuhl": (60, 1.0, 1.0)} # DB, Stunden, m²vorrat = {"Montagestunden": 150, "Plattenmaterial": 240}
-s = pywraplp.Solver.CreateSolver("GLOP")
+s = pywraplp.[Solver](anhang-glossar.md#gloss:solver){.glossar-link}.CreateSolver("[GLOP](anhang-glossar.md#gloss:glop){.glossar-link}")x = {p: s.NumVar(0, s.infinity(), p) for p in produkt}s.Add(sum(x[p] * produkt[p][1] for p in produkt) <= vorrat["Montagestunden"])s.Add(sum(x[p] * produkt[p][2] for p in produkt) <= vorrat["Plattenmaterial"])
@@ -222,7 +222,7 @@ Deckungsbeitrag: 10800 EUR
Eine Klinik muss 200 Pflegekräfte einteilen — unter Berücksichtigung von 15 gesetzlichen Arbeitszeitregeln, Urlaubsanträgen, Qualifikationsstufen und Notfallreserven.
Ein Fondsmanager muss 10 Millionen Euro so auf 50 Aktien aufteilen, dass bei einer erwarteten Mindestrendite von 8 % das Verlustrisiko im schlechtesten Marktszenario minimal bleibt.
-
Ein naiver Ansatz — Brute Force, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
+
Ein naiver Ansatz — Brute Force, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
📖 Definition: Operations Research
Operations Research (OR), deutsch etwa Unternehmensforschung oder mathematische Entscheidungsoptimierung, ist die wissenschaftliche Disziplin, reale Entscheidungsprobleme in formale mathematische Modelle zu überführen und mithilfe exakter oder heuristischer Algorithmen (Solver) die beweisbar beste (optimale) oder eine messbar hochwertige Lösung zu finden.
@@ -333,7 +333,7 @@ Deckungsbeitrag: 10800 EUR
Was Sie in der Abbildung sehen. Der markierte Streifen zwischen n = 15 und n = 20 ist der eigentliche Befund: Dort wird aus einer Kaffeepause ein Menschenleben. Wer eine Maschine kauft, die tausendmal schneller rechnet, verschiebt diese Wand um genau zwei Aufträge.
Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nicht. Selbst eine Beschleunigung um den Faktor eine Million verschiebt die Grenze von n=20 nur auf etwa n=24. Gegen kombinatorisches Wachstum ist Hardware machtlos.
-
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
+
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
#!/usr/bin/env python3# Brute_Force_Vergleich.py
@@ -514,10 +514,10 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
1.5 Historischer Kontext und Evolution
Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man die neu entwickelten Radaranlagen an der Küste optimal? Wie groß müssen Schiffskonvois sein, um U-Boot-Angriffe bei minimalem Geleitschutzaufwand abzuwehren?
-
Wissenschaftler wie George Dantzig, Patrick Blackett und John von Neumann entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den Simplex-Algorithmus zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in Kapitel 5 selbst programmieren werden.
+
Wissenschaftler wie George Dantzig, Patrick Blackett und John von Neumann entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den Simplex-Algorithmus zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in Kapitel 5 selbst programmieren werden.
Heute treibt OR die Kernsysteme moderner Industrien an:
-
Tech-Konzerne: Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
+
Tech-Konzerne: Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
Luftfahrt & Logistik: Crew-Scheduling, Flugzeugumlaufplanung, Paketlogistik (etwa das System ORION von UPS, das durch Routenoptimierung jährlich zweistellige Millionenbeträge an Treibstoff einspart).
@@ -574,7 +574,7 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
Baustein 2 — Parameter / Eingabedaten (c, A, b)
Die unveränderlichen Fakten der Realität: historische Renditen, Maschinenstundensätze, Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen.
-
Die Leitfrage:Was ist gegeben und wird nicht entschieden? Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. Kapitel 18 zeigt am Beispiel der Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
+
Die Leitfrage:Was ist gegeben und wird nicht entschieden? Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. Kapitel 18 zeigt am Beispiel der Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
Baustein 3 — Zielfunktion (f(x))
Ein mathematischer Ausdruck, der die Güte einer Lösung zu einer einzigen Zahl verdichtet:
@@ -599,7 +599,7 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
g_i(\mathbf{x}) \le b_i \qquad \text{bzw.} \qquad h_j(\mathbf{x}) = 0
Harte Nebenbedingungen (hard constraints) sind zwingend: gesetzliche Ruhezeit \ge 11 Stunden, Summe der Portfoliogewichte = 100\,\%, maximales Verlustrisiko \le 5\,\%. Wird eine einzige harte Bedingung verletzt, ist die Lösung mathematisch unzulässig (infeasible) — sie existiert für den Solver schlicht nicht.
-
Weiche Nebenbedingungen (soft constraints) sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über Strafkosten (penalties) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
+
Weiche Nebenbedingungen (soft constraints) sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über Strafkosten (penalties) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
🎯 Merksatz Harte Bedingung = „darf nicht“. Weiche Bedingung = „soll möglichst nicht, sonst kostet es X“. Die Entscheidung zwischen beiden ist eine der folgenreichsten im ganzen Modell: Zu viele harte Bedingungen erzeugen unlösbare Modelle (Kapitel 22), zu wenige erzeugen Lösungen, die niemand akzeptiert.
@@ -1073,12 +1073,12 @@ Vorteil der Optimierung: 300.00 EUR pro Tag (15.0 %)
modell.Add(x_a <= MAX_ARBITRAGE)
Marktlimit als Nebenbedingung
-
Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen
+
Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen
loeser.StatusName(status)
echter Status statt fester Text
-
Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten
+
Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten
assert ...
@@ -1149,7 +1149,7 @@ Vorteil der Optimierung: 300.00 EUR pro Tag (15.0 %)
Aufgabe 1.2 ⭐ — Hart oder weich? Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart oder weich modelliert werden sollte, und begründen Sie: (a) Gesetzliche Ruhezeit von 11 Stunden zwischen zwei Diensten. (b) Frau Meier möchte freitags nicht arbeiten. (c) In jeder Nachtschicht muss mindestens eine examinierte Fachkraft anwesend sein. (d) Die Dienste sollen gleichmäßig über das Team verteilt sein. (e) Niemand arbeitet mehr als 10 Tage am Stück.
Aufgabe 1.3 ⭐⭐ — Kombinatorik selbst rechnen. Ein Speditionsdisponent muss 12 Aufträge auf einen einzigen Lkw in eine Reihenfolge bringen. (a) Wie viele Reihenfolgen gibt es? (b) Der Rechner prüft 5 Millionen Reihenfolgen pro Sekunde. Wie lange dauert das vollständige Durchprobieren? (c) Wie lange dauert es bei 13 Aufträgen? Um welchen Faktor ist das mehr? (d) Wie viele Aufträge könnte man in einer Stunde noch vollständig durchprobieren?
Aufgabe 1.4 ⭐⭐ — Modell lesen. Gegeben sei das Modell \max\ 3x_1 + 5x_2 \quad \text{u. d. N.} \quad x_1 \le 4,\ \ 2x_2 \le 12,\ \ 3x_1 + 2x_2 \le 18,\ \ x_1, x_2 \ge 0. (a) Benennen Sie die vier Bausteine. (b) Ist (2, 6) zulässig? Ist (4, 3) zulässig? Berechnen Sie jeweils Z. (c) Finden Sie durch Probieren die beste ganzzahlige Lösung.
-
Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren. Setzen Sie das Bäckerei-Problem der Handrechnung Zerlegen Sie dieses Problem mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit assert-Anweisungen.
+
Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren. Setzen Sie das Bäckerei-Problem der Handrechnung Zerlegen Sie dieses Problem mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit assert-Anweisungen.
Aufgabe 1.6 ⭐⭐⭐ — Sensitivität durch Ausprobieren. Erweitern Sie Bot_Allokation.py so, dass es das Modell in einer Schleife für RAM-Kapazitäten von 54 bis 72 GB (in Schritten von 2) löst und eine Tabelle RAM | x_A | x_B | Gewinn | Gewinnzuwachs pro zusätzlichem GB ausgibt. (a) Ab welcher RAM-Menge steigt der Gewinn nicht mehr? Warum? (b) Was sagt Ihnen der „Gewinnzuwachs pro GB“ intuitiv? (Der Begriff dafür — Schattenpreis — folgt in Kapitel 5.)
Aufgabe 1.7 ⭐⭐⭐ — Eigenes Problem zerlegen. Wählen Sie ein Entscheidungsproblem aus Ihrem eigenen Alltag oder Beruf (Beispiele: Wochenplan für Sportkurse, Sitzordnung bei einer Feier, Aufteilung eines Budgets auf Projekte, Reihenfolge von Hausarbeiten). Füllen Sie die Vorlage aus Abschnitt 1.6 vollständig aus. Notieren Sie außerdem: Woher kämen die Daten? Wer müsste das Ergebnis akzeptieren? — Heben Sie diese Notiz auf; sie ist der Ausgangspunkt für Ihr eigenes Projekt in der Projektwerkstatt.
Operations Research ist präskriptiv: Es beantwortet nicht, was war oder was kommt, sondern was zu tun ist.
-
Kombinatorische Explosionist eine Wand, keine Steigung. Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
+
Kombinatorische Explosionist eine Wand, keine Steigung. Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
Jedes Modell besteht aus vier Bausteinen: Entscheidungsvariablen, Parameter, Zielfunktion, Nebenbedingungen. Diese Zerlegung ist die eigentliche Arbeit; die Solverwahl ergibt sich danach fast von selbst.
Harte und weiche Bedingungen sind eine Entwurfsentscheidung mit großen Folgen für Lösbarkeit und Akzeptanz.
Ihre Daten liegen schon in Excel.pandas.read_excel() holt sie ab, der Solver rechnet, to_excel() gibt das Ergebnis im gewohnten Format zurück. Der Gewinn gegenüber einer Solver-Mappe ist nicht Rechenleistung, sondern Nachvollziehbarkeit: versionierbar, testbar, hundertmal in einer Schleife ausführbar.
Prüfen Sie jedes Ergebnis — gegen eine Handrechnung, gegen assert-Anweisungen und gegen eine naive Vergleichsstrategie. Ein unerwartet gutes Ergebnis ist ein Fehlerverdacht, keine gute Nachricht.
-
Ausblick.Kapitel 2 liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
+
Ausblick.Kapitel 2 liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
Worum geht es? Um die Eingangsdaten der Portfoliooptimierung — und darum, warum sie trügerisch sind. Wer historische Kovarianzen ungefiltert in einen Optimierer gibt, optimiert Rauschen.
Voraussetzungen:Kapitel 11 (Eigenwerte, positive Definitheit), Grundlagen der Statistik.
-
Danach können Sie: Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage anwenden — und erkennen, wann eine Kovarianzmatrix aus zu wenigen Beobachtungen stammt.
+
Danach können Sie: Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage anwenden — und erkennen, wann eine Kovarianzmatrix aus zu wenigen Beobachtungen stammt.
Das Problem: \mathbf{S} hat \frac{N(N+1)}{2} zu schätzende Parameter, aber nur T \cdot N Datenpunkte. Bei N = 50 Titeln sind das 1275 Parameter — geschätzt aus einem Jahr Daten (T = 252).
+
Das Problem: \mathbf{S} hat \frac{N(N+1)}{2} zu schätzende Parameter, aber nur T \cdot N Datenpunkte. Bei N = 50 Titeln sind das 1275 Parameter — geschätzt aus einem Jahr Daten (T = 252).
Die Zufallsmatrizentheorie (Random Matrix Theory, Marchenko-Pastur-Gesetz) zeigt: Die kleinsten Eigenwerte von \mathbf{S} werden systematisch unterschätzt, die größten überschätzt. Und genau das ist fatal, denn:
+
Die Zufallsmatrizentheorie (Random Matrix Theory, Marchenko-Pastur-Gesetz) zeigt: Die kleinsten Eigenwerte von \mathbf{S} werden systematisch unterschätzt, die größten überschätzt. Und genau das ist fatal, denn:
🎯 Der Error-Maximizer-Effekt Ein Risikominimierer sucht die Richtungen mit der kleinsten Varianz — also genau jene Eigenrichtungen, deren Eigenwerte am stärksten nach unten verzerrt sind. Die Optimierung greift damit zielsicher in das Schätzrauschen hinein. Markowitz-Optimierung ist deshalb kein Fehlerdämpfer, sondern ein Fehlerverstärker.
Diese Tabelle ist das Kernargument des ganzen Kapitels. Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit 75,5 % mehr Risiko als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das geschätzte Risiko — und die Differenz ist der Schätzfehler, in den er hineinoptimiert hat.
+
Diese Tabelle ist das Kernargument des ganzen Kapitels. Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit 75,5 % mehr Risiko als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das geschätzte Risiko — und die Differenz ist der Schätzfehler, in den er hineinoptimiert hat.
Drei Beobachtungen:
Der Schaden wächst dramatisch, je knapper die Daten. Von T/N = 31{,}5 (+1,6 %) bis T/N = 1{,}5 (+75,5 %) ist es kein gleitender Übergang, sondern eine Explosion.
Aufgabe 18.2 ⭐ — Mittelwertfalle. Ein Fonds meldet Jahresrenditen von +60\,\%, -40\,\%, +60\,\%, -40\,\%. (a) Wie hoch ist das arithmetische Mittel? (b) Wie viel Kapital hat ein Anleger nach 4 Jahren aus 10 000 €? (c) Welche jährliche Rendite entspricht dem tatsächlich?
Aufgabe 18.3 ⭐⭐ — Konditionszahl verstehen. Erzeugen Sie eine Kovarianzmatrix für N = 30 Titel aus T = 40, T = 100 und T = 1000 simulierten Beobachtungen. Berechnen Sie jeweils kleinsten Eigenwert und Konditionszahl. Ab welchem T wird die Matrix brauchbar?
Aufgabe 18.4 ⭐⭐ — Spaltenreihenfolge prüfen. Laden Sie fünf Ticker in einer bewusst unsortierten Reihenfolge. Zeigen Sie, dass raw["Close"].columns abweicht, und dass raw["Close"][tickers] das behebt. Was passiert, wenn ein Ticker nicht existiert?
-
Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen. Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von \delta nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit sklearn anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines GMV-Portfolios (nach dem Muster von Schaetzrauschen_Demo.py), (c) der Stabilität der Gewichte über rollierende Fenster.
+
Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen. Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von \delta nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit sklearn anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines GMV-Portfolios (nach dem Muster von Schaetzrauschen_Demo.py), (c) der Stabilität der Gewichte über rollierende Fenster.
Aufgabe 18.6 ⭐⭐⭐ — Error-Maximizer selbst messen. Erweitern Sie Schaetzrauschen_Demo.py um eine dritte Variante: die Gleichgewichtung (w_i = 1/N), die überhaupt keine Schätzung benötigt. (a) Ab welchem T/N schlägt die Stichproben-Optimierung die Gleichgewichtung? (b) Ab welchem T/N schlägt Ledoit-Wolf sie? (c) Was folgt daraus für die Praxis?
Worum geht es? Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
+
Worum geht es? Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
Voraussetzungen:Kapitel 1. Lineare Algebra wird hier von Grund auf wiederholt.
Danach können Sie: Ein Modell in Matrixform aufschreiben, den zulässigen Bereich geometrisch deuten, beurteilen ob ein Problem konvex ist — und einschätzen, wie viele Stellen Ihres Ergebnisses überhaupt belastbar sind.
Zeitbedarf: ca. 5 Stunden.
@@ -117,7 +117,7 @@
2.1 In 5 Minuten gelöst
-
Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
+
Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
🚀 In 5 Minuten gelöst: Das Optimum sitzt immer in einer Ecke
@@ -146,8 +146,8 @@ Ecke (6.0, 0.0) -> Z = 18.00
Ecke (0.0, 0.0) -> Z = 0.00
-
Das Optimum lautet (0, 8) mit Z = 40 — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der Fundamentalsatz der linearen Optimierung, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
-
Warum funktioniert das? Weil der zulässige Bereich ein konvexes Polyeder ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der Simplex-Algorithmus in Kapitel 5 tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
+
Das Optimum lautet (0, 8) mit Z = 40 — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der Fundamentalsatz der linearen Optimierung, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
+
Warum funktioniert das? Weil der zulässige Bereich ein konvexes Polyeder ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der Simplex-Algorithmus in Kapitel 5 tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
⚠️ Und wo ist der Haken? Bei zwei Variablen gibt es 6 Eckenkandidaten, bei 50 Variablen und 50 Bedingungen sind es \binom{100}{50} \approx 10^{29}. Der Fundamentalsatz sagt uns wo wir suchen müssen — nicht, dass die Suche leicht wird.
@@ -160,7 +160,7 @@ Ecke (0.0, 0.0) -> Z = 0.00
… den Fundamentalsatz der linearen Optimierung anwenden, um Kandidaten für das Optimum zu finden, ohne alles durchzuprobieren.
… prüfen, ob eine Menge bzw. eine Funktion konvex ist, und begründen, warum das für die Lösbarkeit entscheidend ist.
… einen zweidimensionalen Lösungsraum mit matplotlib zeichnen und daraus die optimale Ecke ablesen.
-
… die Konditionszahl\kappa(\mathbf{A}) berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
+
… die Konditionszahl\kappa(\mathbf{A}) berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
… erklären, warum ein Solver-Ergebnis von 0.99999998 niemals mit int() in eine ganze Zahl verwandelt werden darf.
@@ -169,7 +169,7 @@ Ecke (0.0, 0.0) -> Z = 0.00
Die lineare Algebra löst dieses Problem, indem sie viele gleichartige Zahlen zu einem Objekt bündelt. Das ist derselbe Gedanke wie eine Liste in Python: Statt preis_1, preis_2, …, preis_1000 schreibt man preise und arbeitet mit dem Ganzen.
Die Bausteine
Sei n die Anzahl der Entscheidungsvariablen und m die Anzahl der Nebenbedingungen.
📐 Formel-Lesehilfe * \mathbf{c}^\top — der Ertragsvektor, „umgekippt“ zu einer Zeile. * \mathbf{c}^\top \mathbf{x} — Skalarprodukt: Multipliziere jedes c_j mit dem zugehörigen x_j und addiere alles. Das Ergebnis ist eine einzige Zahl. * \sum_{j=1}^n — „addiere für j = 1 bis j = n“.
Ohne Formel gesagt: „Nimm von jedem Produkt die hergestellte Menge mal den Gewinn pro Stück und zähle alles zusammen.“ Im Bot-Beispiel: \mathbf{c} = (150, 250)^\top, \mathbf{x} = (7, 5)^\top, also \mathbf{c}^\top\mathbf{x} = 150 \cdot 7 + 250 \cdot 5 = 2300.
-
Technologiematrix — wer verbraucht wie viel wovon:
📐 Formel-LesehilfeJede Zeile ist eine Ressource, jede Spalte eine Variable. Der Eintrag a_{ij} beantwortet: „Wie viel von Ressource i verbraucht eine Einheit von Variable j?“
Im Bot-Beispiel: \mathbf{A} = \begin{pmatrix} 2 & 5 \\ 4 & 6 \\ 1 & 0 \end{pmatrix} \begin{matrix} \leftarrow \text{vCPU} \\ \leftarrow \text{RAM} \\ \leftarrow \text{Liquidität} \end{matrix} Die erste Zeile (2, 5) heißt: Ein A-Bot braucht 2 vCPUs, ein B-Bot braucht 5. Die dritte Zeile (1, 0) heißt: Die Liquiditätsgrenze zählt nur A-Bots, B-Bots gar nicht.
mit Diagonalmatrizen \mathbf{D}_r (Zeilen) und \mathbf{D}_c (Spalten). Das Ruiz-Verfahren bestimmt diese Faktoren iterativ: In jedem Durchlauf wird jede Zeile durch die Wurzel ihres größten Betrags geteilt, danach jede Spalte. Nach wenigen Durchläufen liegen alle Zeilen- und Spaltenmaxima bei 1.
Wirtschaftlich passiert dabei nichts: Eine Zeilenskalierung heißt „diese Nebenbedingung in einer anderen Einheit messen“, eine Spaltenskalierung „diese Variable in einer anderen Einheit messen“. Die Lösung rechnet man mit \mathbf{x} = \mathbf{D}_c\,\tilde{\mathbf{x}} zurück.
-
🎯 Merksatz Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
+
🎯 Merksatz Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
Das Experiment
Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und schließt mit der praktischen Konsequenz, die Sie in Kapitel 6 wieder brauchen werden: warum eine Binärvariable mit dem Wert 0.99999998 niemals mit int() gerundet werden darf.
@@ -1233,7 +1233,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb, Technik und Finanzmärkte
-
Ein praxisorientiertes Kompendium für Entscheider mit Constraint Programming, Vektor- und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
+
Ein praxisorientiertes Kompendium für Entscheider mit Constraint Programming, Vektor- und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten.
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
-
Stand: 10. September 2026 v16.09
+
Stand: 10. September 2026 v17.38
x
„iks“
-
Entscheidungsvariable(n) — das, was der Solver festlegen darf
+
Entscheidungsvariable(n) — das, was der Solver festlegen darf
@@ -2605,7 +2605,7 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
-
⚠️ Achtung, Doppelbelegungen. In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: \lambda ist in Kapitel 5 und Kapitel 11 ein Lagrange-Multiplikator/Schattenpreis, in Kapitel 19 und Kapitel 20 ein frei gewählter Risikoaversionsparameter. \gamma ist in Kapitel 13 der Diskontfaktor, in Kapitel 20 die VaR-Hilfsvariable. S bezeichnet als Matrix \mathbf{S} die Stichprobenkovarianz, als Skalar S die Szenarienzahl. \pi ist in Kapitel 9 eine Reihenfolge, in Kapitel 10 ein Dualpreis — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
+
⚠️ Achtung, Doppelbelegungen. In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: \lambda ist in Kapitel 5 und Kapitel 11 ein Lagrange-Multiplikator/Schattenpreis, in Kapitel 19 und Kapitel 20 ein frei gewählter Risikoaversionsparameter. \gamma ist in Kapitel 13 der Diskontfaktor, in Kapitel 20 die VaR-Hilfsvariable. S bezeichnet als Matrix \mathbf{S} die Stichprobenkovarianz, als Skalar S die Szenarienzahl. \pi ist in Kapitel 9 eine Reihenfolge, in Kapitel 10 ein Dualpreis — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
2. Abkürzungen — vollständig ausgeschrieben
@@ -2657,55 +2657,55 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
@@ -3153,7 +3153,7 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
📌 Kapitel auf einen Blick
Worum geht es? Was Operations Research ist, warum Ausprobieren ab einer gewissen Problemgröße hoffnungslos wird, und aus welchen vier Bausteinen jedes Optimierungsmodell besteht. Los geht es mit einem Problem, das Sie in fünf Minuten selbst lösen.
Voraussetzungen: Keine. Dies ist der Einstieg.
-
Danach können Sie: Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
+
Danach können Sie: Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
Zeitbedarf: ca. 3,5 Stunden inklusive Übungen.
Programme: Bot_Allokation.py
@@ -3197,7 +3197,7 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
produkt = {"Tisch": (240, 3.0, 6.0), "Stuhl": (60, 1.0, 1.0)} # DB, Stunden, m²vorrat = {"Montagestunden": 150, "Plattenmaterial": 240}
-s = pywraplp.Solver.CreateSolver("GLOP")
+s = pywraplp.[Solver](#gloss:solver){.glossar-link}.CreateSolver("[GLOP](#gloss:glop){.glossar-link}")x = {p: s.NumVar(0, s.infinity(), p) for p in produkt}s.Add(sum(x[p] * produkt[p][1] for p in produkt) <= vorrat["Montagestunden"])s.Add(sum(x[p] * produkt[p][2] for p in produkt) <= vorrat["Plattenmaterial"])
@@ -3265,7 +3265,7 @@ Deckungsbeitrag: 10800 EUR
Eine Klinik muss 200 Pflegekräfte einteilen — unter Berücksichtigung von 15 gesetzlichen Arbeitszeitregeln, Urlaubsanträgen, Qualifikationsstufen und Notfallreserven.
Ein Fondsmanager muss 10 Millionen Euro so auf 50 Aktien aufteilen, dass bei einer erwarteten Mindestrendite von 8 % das Verlustrisiko im schlechtesten Marktszenario minimal bleibt.
-
Ein naiver Ansatz — Brute Force, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
+
Ein naiver Ansatz — Brute Force, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
📖 Definition: Operations Research
Operations Research (OR), deutsch etwa Unternehmensforschung oder mathematische Entscheidungsoptimierung, ist die wissenschaftliche Disziplin, reale Entscheidungsprobleme in formale mathematische Modelle zu überführen und mithilfe exakter oder heuristischer Algorithmen (Solver) die beweisbar beste (optimale) oder eine messbar hochwertige Lösung zu finden.
@@ -3376,7 +3376,7 @@ Deckungsbeitrag: 10800 EUR
Was Sie in der Abbildung sehen. Der markierte Streifen zwischen n = 15 und n = 20 ist der eigentliche Befund: Dort wird aus einer Kaffeepause ein Menschenleben. Wer eine Maschine kauft, die tausendmal schneller rechnet, verschiebt diese Wand um genau zwei Aufträge.
Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nicht. Selbst eine Beschleunigung um den Faktor eine Million verschiebt die Grenze von n=20 nur auf etwa n=24. Gegen kombinatorisches Wachstum ist Hardware machtlos.
-
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
+
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
#!/usr/bin/env python3# Brute_Force_Vergleich.py
@@ -3557,10 +3557,10 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
1.5 Historischer Kontext und Evolution
Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man die neu entwickelten Radaranlagen an der Küste optimal? Wie groß müssen Schiffskonvois sein, um U-Boot-Angriffe bei minimalem Geleitschutzaufwand abzuwehren?
-
Wissenschaftler wie George Dantzig, Patrick Blackett und John von Neumann entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den Simplex-Algorithmus zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in Kapitel 5 selbst programmieren werden.
+
Wissenschaftler wie George Dantzig, Patrick Blackett und John von Neumann entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den Simplex-Algorithmus zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in Kapitel 5 selbst programmieren werden.
Heute treibt OR die Kernsysteme moderner Industrien an:
-
Tech-Konzerne: Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
+
Tech-Konzerne: Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
Luftfahrt & Logistik: Crew-Scheduling, Flugzeugumlaufplanung, Paketlogistik (etwa das System ORION von UPS, das durch Routenoptimierung jährlich zweistellige Millionenbeträge an Treibstoff einspart).
@@ -3617,7 +3617,7 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
Baustein 2 — Parameter / Eingabedaten (c, A, b)
Die unveränderlichen Fakten der Realität: historische Renditen, Maschinenstundensätze, Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen.
-
Die Leitfrage:Was ist gegeben und wird nicht entschieden? Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. Kapitel 18 zeigt am Beispiel der Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
+
Die Leitfrage:Was ist gegeben und wird nicht entschieden? Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. Kapitel 18 zeigt am Beispiel der Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
Baustein 3 — Zielfunktion (f(x))
Ein mathematischer Ausdruck, der die Güte einer Lösung zu einer einzigen Zahl verdichtet:
@@ -3642,7 +3642,7 @@ Der Solver löst dieselbe Instanz in Sekundenbruchteilen.
g_i(\mathbf{x}) \le b_i \qquad \text{bzw.} \qquad h_j(\mathbf{x}) = 0
Harte Nebenbedingungen (hard constraints) sind zwingend: gesetzliche Ruhezeit \ge 11 Stunden, Summe der Portfoliogewichte = 100\,\%, maximales Verlustrisiko \le 5\,\%. Wird eine einzige harte Bedingung verletzt, ist die Lösung mathematisch unzulässig (infeasible) — sie existiert für den Solver schlicht nicht.
-
Weiche Nebenbedingungen (soft constraints) sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über Strafkosten (penalties) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
+
Weiche Nebenbedingungen (soft constraints) sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über Strafkosten (penalties) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
🎯 Merksatz Harte Bedingung = „darf nicht“. Weiche Bedingung = „soll möglichst nicht, sonst kostet es X“. Die Entscheidung zwischen beiden ist eine der folgenreichsten im ganzen Modell: Zu viele harte Bedingungen erzeugen unlösbare Modelle (Kapitel 22), zu wenige erzeugen Lösungen, die niemand akzeptiert.
@@ -4114,12 +4114,12 @@ Vorteil der Optimierung: 300.00 EUR pro Tag (15.0 %)
modell.Add(x_a <= MAX_ARBITRAGE)
Marktlimit als Nebenbedingung
-
Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen
+
Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen
loeser.StatusName(status)
echter Status statt fester Text
-
Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten
+
Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten
assert ...
@@ -4189,7 +4189,7 @@ Vorteil der Optimierung: 300.00 EUR pro Tag (15.0 %)
Aufgabe 1.2 ⭐ — Hart oder weich? Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart oder weich modelliert werden sollte, und begründen Sie: (a) Gesetzliche Ruhezeit von 11 Stunden zwischen zwei Diensten. (b) Frau Meier möchte freitags nicht arbeiten. (c) In jeder Nachtschicht muss mindestens eine examinierte Fachkraft anwesend sein. (d) Die Dienste sollen gleichmäßig über das Team verteilt sein. (e) Niemand arbeitet mehr als 10 Tage am Stück.
Aufgabe 1.3 ⭐⭐ — Kombinatorik selbst rechnen. Ein Speditionsdisponent muss 12 Aufträge auf einen einzigen Lkw in eine Reihenfolge bringen. (a) Wie viele Reihenfolgen gibt es? (b) Der Rechner prüft 5 Millionen Reihenfolgen pro Sekunde. Wie lange dauert das vollständige Durchprobieren? (c) Wie lange dauert es bei 13 Aufträgen? Um welchen Faktor ist das mehr? (d) Wie viele Aufträge könnte man in einer Stunde noch vollständig durchprobieren?
Aufgabe 1.4 ⭐⭐ — Modell lesen. Gegeben sei das Modell \max\ 3x_1 + 5x_2 \quad \text{u. d. N.} \quad x_1 \le 4,\ \ 2x_2 \le 12,\ \ 3x_1 + 2x_2 \le 18,\ \ x_1, x_2 \ge 0. (a) Benennen Sie die vier Bausteine. (b) Ist (2, 6) zulässig? Ist (4, 3) zulässig? Berechnen Sie jeweils Z. (c) Finden Sie durch Probieren die beste ganzzahlige Lösung.
-
Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren. Setzen Sie das Bäckerei-Problem der Handrechnung Zerlegen Sie dieses Problem mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit assert-Anweisungen.
+
Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren. Setzen Sie das Bäckerei-Problem der Handrechnung Zerlegen Sie dieses Problem mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit assert-Anweisungen.
Aufgabe 1.6 ⭐⭐⭐ — Sensitivität durch Ausprobieren. Erweitern Sie Bot_Allokation.py so, dass es das Modell in einer Schleife für RAM-Kapazitäten von 54 bis 72 GB (in Schritten von 2) löst und eine Tabelle RAM | x_A | x_B | Gewinn | Gewinnzuwachs pro zusätzlichem GB ausgibt. (a) Ab welcher RAM-Menge steigt der Gewinn nicht mehr? Warum? (b) Was sagt Ihnen der „Gewinnzuwachs pro GB“ intuitiv? (Der Begriff dafür — Schattenpreis — folgt in Kapitel 5.)
Aufgabe 1.7 ⭐⭐⭐ — Eigenes Problem zerlegen. Wählen Sie ein Entscheidungsproblem aus Ihrem eigenen Alltag oder Beruf (Beispiele: Wochenplan für Sportkurse, Sitzordnung bei einer Feier, Aufteilung eines Budgets auf Projekte, Reihenfolge von Hausarbeiten). Füllen Sie die Vorlage aus Abschnitt 1.6 vollständig aus. Notieren Sie außerdem: Woher kämen die Daten? Wer müsste das Ergebnis akzeptieren? — Heben Sie diese Notiz auf; sie ist der Ausgangspunkt für Ihr eigenes Projekt in der Projektwerkstatt.
Operations Research ist präskriptiv: Es beantwortet nicht, was war oder was kommt, sondern was zu tun ist.
-
Kombinatorische Explosionist eine Wand, keine Steigung. Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
+
Kombinatorische Explosionist eine Wand, keine Steigung. Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
Jedes Modell besteht aus vier Bausteinen: Entscheidungsvariablen, Parameter, Zielfunktion, Nebenbedingungen. Diese Zerlegung ist die eigentliche Arbeit; die Solverwahl ergibt sich danach fast von selbst.
Harte und weiche Bedingungen sind eine Entwurfsentscheidung mit großen Folgen für Lösbarkeit und Akzeptanz.
Ihre Daten liegen schon in Excel.pandas.read_excel() holt sie ab, der Solver rechnet, to_excel() gibt das Ergebnis im gewohnten Format zurück. Der Gewinn gegenüber einer Solver-Mappe ist nicht Rechenleistung, sondern Nachvollziehbarkeit: versionierbar, testbar, hundertmal in einer Schleife ausführbar.
Prüfen Sie jedes Ergebnis — gegen eine Handrechnung, gegen assert-Anweisungen und gegen eine naive Vergleichsstrategie. Ein unerwartet gutes Ergebnis ist ein Fehlerverdacht, keine gute Nachricht.
-
Ausblick.Kapitel 2 liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
+
Ausblick.Kapitel 2 liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität
📌 Kapitel auf einen Blick
-
Worum geht es? Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
+
Worum geht es? Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
Voraussetzungen:Kapitel 1. Lineare Algebra wird hier von Grund auf wiederholt.
Danach können Sie: Ein Modell in Matrixform aufschreiben, den zulässigen Bereich geometrisch deuten, beurteilen ob ein Problem konvex ist — und einschätzen, wie viele Stellen Ihres Ergebnisses überhaupt belastbar sind.
Zeitbedarf: ca. 5 Stunden.
@@ -4475,7 +4475,7 @@ Deckungsbeitrag: 18600 EUR
2.1 In 5 Minuten gelöst
-
Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
+
Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
🚀 In 5 Minuten gelöst: Das Optimum sitzt immer in einer Ecke
Ein Betrieb fertigt zwei Produkte. Zwei Ressourcen begrenzen ihn:
@@ -4502,8 +4502,8 @@ Ecke (0.0, 8.0) -> Z = 40.00
Ecke (6.0, 0.0) -> Z = 18.00
Ecke (0.0, 0.0) -> Z = 0.00
-
Das Optimum lautet (0, 8) mit Z = 40 — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der Fundamentalsatz der linearen Optimierung, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
-
Warum funktioniert das? Weil der zulässige Bereich ein konvexes Polyeder ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der Simplex-Algorithmus in Kapitel 5 tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
+
Das Optimum lautet (0, 8) mit Z = 40 — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der Fundamentalsatz der linearen Optimierung, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
+
Warum funktioniert das? Weil der zulässige Bereich ein konvexes Polyeder ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der Simplex-Algorithmus in Kapitel 5 tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
⚠️ Und wo ist der Haken? Bei zwei Variablen gibt es 6 Eckenkandidaten, bei 50 Variablen und 50 Bedingungen sind es \binom{100}{50} \approx 10^{29}. Der Fundamentalsatz sagt uns wo wir suchen müssen — nicht, dass die Suche leicht wird.
@@ -4516,7 +4516,7 @@ Ecke (0.0, 0.0) -> Z = 0.00
… den Fundamentalsatz der linearen Optimierung anwenden, um Kandidaten für das Optimum zu finden, ohne alles durchzuprobieren.
… prüfen, ob eine Menge bzw. eine Funktion konvex ist, und begründen, warum das für die Lösbarkeit entscheidend ist.
… einen zweidimensionalen Lösungsraum mit matplotlib zeichnen und daraus die optimale Ecke ablesen.
-
… die Konditionszahl\kappa(\mathbf{A}) berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
+
… die Konditionszahl\kappa(\mathbf{A}) berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
… erklären, warum ein Solver-Ergebnis von 0.99999998 niemals mit int() in eine ganze Zahl verwandelt werden darf.
@@ -4525,7 +4525,7 @@ Ecke (0.0, 0.0) -> Z = 0.00
Die lineare Algebra löst dieses Problem, indem sie viele gleichartige Zahlen zu einem Objekt bündelt. Das ist derselbe Gedanke wie eine Liste in Python: Statt preis_1, preis_2, …, preis_1000 schreibt man preise und arbeitet mit dem Ganzen.
Die Bausteine
Sei n die Anzahl der Entscheidungsvariablen und m die Anzahl der Nebenbedingungen.
📐 Formel-Lesehilfe * \mathbf{c}^\top — der Ertragsvektor, „umgekippt“ zu einer Zeile. * \mathbf{c}^\top \mathbf{x} — Skalarprodukt: Multipliziere jedes c_j mit dem zugehörigen x_j und addiere alles. Das Ergebnis ist eine einzige Zahl. * \sum_{j=1}^n — „addiere für j = 1 bis j = n“.
Ohne Formel gesagt: „Nimm von jedem Produkt die hergestellte Menge mal den Gewinn pro Stück und zähle alles zusammen.“ Im Bot-Beispiel: \mathbf{c} = (150, 250)^\top, \mathbf{x} = (7, 5)^\top, also \mathbf{c}^\top\mathbf{x} = 150 \cdot 7 + 250 \cdot 5 = 2300.
-
Technologiematrix — wer verbraucht wie viel wovon:
📐 Formel-LesehilfeJede Zeile ist eine Ressource, jede Spalte eine Variable. Der Eintrag a_{ij} beantwortet: „Wie viel von Ressource i verbraucht eine Einheit von Variable j?“
Im Bot-Beispiel: \mathbf{A} = \begin{pmatrix} 2 & 5 \\ 4 & 6 \\ 1 & 0 \end{pmatrix} \begin{matrix} \leftarrow \text{vCPU} \\ \leftarrow \text{RAM} \\ \leftarrow \text{Liquidität} \end{matrix} Die erste Zeile (2, 5) heißt: Ein A-Bot braucht 2 vCPUs, ein B-Bot braucht 5. Die dritte Zeile (1, 0) heißt: Die Liquiditätsgrenze zählt nur A-Bots, B-Bots gar nicht.
mit Diagonalmatrizen \mathbf{D}_r (Zeilen) und \mathbf{D}_c (Spalten). Das Ruiz-Verfahren bestimmt diese Faktoren iterativ: In jedem Durchlauf wird jede Zeile durch die Wurzel ihres größten Betrags geteilt, danach jede Spalte. Nach wenigen Durchläufen liegen alle Zeilen- und Spaltenmaxima bei 1.
Wirtschaftlich passiert dabei nichts: Eine Zeilenskalierung heißt „diese Nebenbedingung in einer anderen Einheit messen“, eine Spaltenskalierung „diese Variable in einer anderen Einheit messen“. Die Lösung rechnet man mit \mathbf{x} = \mathbf{D}_c\,\tilde{\mathbf{x}} zurück.
-
🎯 Merksatz Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
+
🎯 Merksatz Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
Das Experiment
Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und schließt mit der praktischen Konsequenz, die Sie in Kapitel 6 wieder brauchen werden: warum eine Binärvariable mit dem Wert 0.99999998 niemals mit int() gerundet werden darf.
@@ -5586,7 +5586,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
Beim Abtippen aus der Tabelle liest er sie spaltenweise statt zeilenweise ein:
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}c = np.array([3.0, 5.0])A = np.array([[1.0, 3.0], # spaltenweise abgetippt statt zeilenweise
@@ -5639,7 +5639,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
📌 Kapitel auf einen Blick
Worum geht es? Warum es für Optimierung in Python mehrere konkurrierende Bibliotheken gibt, was sie unterscheidet, und wie Sie in unter einer Minute die richtige auswählen.
Danach können Sie: Für ein gegebenes Problem begründet einen Solver wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
+
Danach können Sie: Für ein gegebenes Problem begründet einen Solver wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
Zeitbedarf: ca. 4 Stunden.
Programme: Ein_System_Vier_Ansaetze.py
@@ -5651,7 +5651,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
3.1 In 5 Minuten gelöst
-
🚀 In 5 Minuten gelöst: Ein LP ohne jede Installation
+
🚀 In 5 Minuten gelöst: Ein LP ohne jede Installation
Ein Futtermittelhersteller mischt zwei Rohstoffe zu möglichst geringen Kosten. Jeder Kilogramm Mischung muss mindestens 20 g Protein und 5 g Fett enthalten.
@@ -5677,8 +5677,8 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
-
Sie brauchen dafür keine zusätzliche Bibliothek — SciPy genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
-
from scipy.optimize import linprog
+
Sie brauchen dafür keine zusätzliche Bibliothek — SciPy genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
+
from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}# linprog MINIMIERT und kennt nur "<=". Mindestgehalte werden also negiert.res = linprog(c=[0.42, 0.88], # Kosten je kg
@@ -5690,18 +5690,18 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
print(res.message)print(f"Weizen {res.x[0]:.3f} kg, Soja {res.x[1]:.3f} kg -> {res.fun:.4f} EUR/kg")
Ausgabe:
-
Optimization terminated successfully. (HiGHS Status 7: Optimal)
+
Optimization terminated successfully. ([HiGHS](#gloss:highs){.glossar-link} Status 7: Optimal)
Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
-
Sie haben soeben HiGHS benutzt — denselben C++-Solver, der auch hinter highspy, hinter CVXPY und in vielen kommerziellen Systemen steckt. scipy.optimize.linprog ist nur die dünnste denkbare Hülle darum.
+
Sie haben soeben HiGHS benutzt — denselben C++-Solver, der auch hinter highspy, hinter CVXPY und in vielen kommerziellen Systemen steckt. scipy.optimize.linprog ist nur die dünnste denkbare Hülle darum.
Das ist die zentrale Botschaft dieses Kapitels: Modellierungsschicht und Solver sind zwei verschiedene Dinge. Die Bibliothek, in der Sie Ihr Modell hinschreiben, bestimmt, wie angenehm die Arbeit ist. Der Solver dahinter bestimmt, wie schnell gerechnet wird. Beide lassen sich unabhängig voneinander tauschen — und genau davon handelt der Rest des Kapitels.
⚠️ Zwei Stolpersteine stecken schon in diesen sechs Zeilen
-
linprogminimiert immer. Wer maximieren will, negiert die Zielfunktion — und darf nicht vergessen, das Ergebnis zurückzudrehen.
+
linprogminimiert immer. Wer maximieren will, negiert die Zielfunktion — und darf nicht vergessen, das Ergebnis zurückzudrehen.
linprog kennt nur <=. Ein Mindestgehalt „\ge 20“ wird zu „-12x_1 - 44x_2 \le -20“. Ein Vorzeichenfehler an dieser Stelle liefert eine perfekt aussehende Lösung, die das Gegenteil des Gewollten erfüllt.
-
Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo benutzt, in der >= einfach >= heißt. Das ist ein Hauptgrund, warum es sie gibt.
+
Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo benutzt, in der >= einfach >= heißt. Das ist ein Hauptgrund, warum es sie gibt.
3.2 Lernziele
@@ -5712,15 +5712,15 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
… dasselbe lineare Programm in scipy.optimize, highspy, ortools und cvxpy formulieren.
… einschätzen, wann sich der Aufwand einer Low-Level-Schnittstelle lohnt und wann nicht.
-
… Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
-
… Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
+
… Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
+
… Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
3.3 Die Zwei-Schichten-Architektur
Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbst. Man nutzt eine zweischichtige Architektur:
Modellierungsschicht (Frontend, DSL): Python-Bibliotheken, mit denen Entscheidungsvariablen, Zielfunktion und Nebenbedingungen in mathematiknaher Syntax formuliert werden. Hier arbeiten Sie.
-
Solver-Schicht (Backend, Engine): Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen (Dual Simplex, Interior-Point, Branch-and-Cut, CDCL-SAT-Suche).
+
Solver-Schicht (Backend, Engine): Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen (Dual Simplex, Interior-Point, Branch-and-Cut, CDCL-SAT-Suche).
@@ -5729,7 +5729,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
🎯 Merksatz Die Modellierungsschicht bestimmt, wie angenehm Sie arbeiten. Die Solver-Schicht bestimmt, wie schnell gerechnet wird. Beides ist entkoppelt — man kann dieselbe CVXPY-Formulierung mit fünf verschiedenen Solvern lösen.
-
Warum diese Trennung nützlich ist: Ein CVXPY-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
+
Warum diese Trennung nützlich ist: Ein CVXPY-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
3.4 Die Werkzeuge im Vergleich
@@ -5756,27 +5756,27 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
@@ -5789,7 +5789,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
Die Entscheidung in drei Fragen
Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:
-
Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen? → Ja: OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → Nein: weiter zu Frage 2.
+
Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen? → Ja:OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → Nein: weiter zu Frage 2.
Frage 2: Ist die Zielfunktion linear? → Ja:scipy.optimize.linprog (klein) oder highspy (groß, wiederholt). → Nein, aber konvex (Quadrate, Normen, log, exp in konvexer Kombination): CVXPY. → Nein und nicht konvex:scipy.optimize.minimize — im Bewusstsein, dass nur ein lokales Optimum herauskommt.
Frage 3: Ist es ein Routing-Problem mit Fahrzeugen, Depots und Zeitfenstern? → Ja: OR-Tools Routing Library (nicht CP-SAT von Hand nachbauen).
@@ -5979,7 +5979,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
⚠️ max_tasks_per_child=1 ist nicht optional Ein Pool ohne diese Angabe ist der Normalfall — er soll seine Arbeiter ja wiederverwenden. Wer die Isolation über einen Pool herstellt und das vergisst, hat einen Prozesswechsel programmiert, aber keine Isolation gewonnen: Die zweite Aufgabe landet im selben Interpreter wie die erste. Der Absturz kommt dann nicht beim ersten Solver, sondern beim zweiten — und sieht aus wie ein Problem des zweiten.
-
Denselben Aufbau verwenden Solverwechsel_CPSAT_HiGHS.py (Kapitel 22) und Benchmark_Skalierung.py (Kapitel 23). Dort wandern zusätzlich Datenobjekte über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen (Abschnitt 22.6).
+
Denselben Aufbau verwenden Solverwechsel_CPSAT_HiGHS.py (Kapitel 22) und Benchmark_Skalierung.py (Kapitel 23). Dort wandern zusätzlich Datenobjekte über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen (Abschnitt 22.6).
#!/usr/bin/env python3# Ein_System_Vier_Ansaetze.py
@@ -6169,7 +6169,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
highspy
~15
-
Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen.
+
Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen.
cvxpy
@@ -6228,7 +6228,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
Vergessen, dass linprog minimiert. Der häufigste Fehler überhaupt. Symptom: Der „optimale“ Gewinn ist erstaunlich niedrig oder null.
CVXPY für ein nicht-konvexes Problem verwenden. CVXPY lehnt das ab mit DCPError: Problem does not follow DCP rules. Das ist ein Feature, keine Einschränkung: Der Fehler sagt Ihnen, dass Ihre Formulierung keine Optimalitätsgarantie hätte.
CP-SAT mit kontinuierlichen Variablen füttern. CP-SAT kennt nur ganze Zahlen. Wer Euro-Beträge modelliert, rechnet in Cent (Ganzzahl) — oder nimmt einen LP-Solver.
-
Für jeden Lauf ein neues Modell bauen. Bei 60 Backtest-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. highspy und CVXPY-Parameter erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe Kapitel 19 und Kapitel 21).
+
Für jeden Lauf ein neues Modell bauen. Bei 60 Backtest-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. highspy und CVXPY-Parameter erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe Kapitel 19 und Kapitel 21).
@@ -6260,7 +6260,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
🐛 Finde den Denkfehler: Der Solver, der angeblich dreimal schneller ist
-
Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins Protokoll: „CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“ Gemessen wurde so:
+
Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins Protokoll: „CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“ Gemessen wurde so:
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
-
1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt? (a) CVXPY — es prüft die Konvexität automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) scipy.optimize.minimize — es kann beliebige Zielfunktionen.
+
1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt? (a) CVXPY — es prüft die Konvexität automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) scipy.optimize.minimize — es kann beliebige Zielfunktionen.
2. Ihr Modell mit 80 000 Variablen braucht 40 Sekunden, davon 32 Sekunden bis zum ersten Solver-Aufruf. Was ist die wirksamste Maßnahme? (a) Einen kommerziellen Solver lizenzieren. (b) Das Zeitlimit des Solvers heraufsetzen. (c) Den Modellaufbau vektorisieren — der Solver ist mit 8 Sekunden gar nicht der Engpass.
-
3. Was haben scipy.optimize.linprog, highspy, CVXPY (mit Standardeinstellung) und Pyomo (mit appsi_highs) gemeinsam? (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
+
3. Was haben scipy.optimize.linprog, highspy, CVXPY (mit Standardeinstellung) und Pyomo (mit appsi_highs) gemeinsam? (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
Worum geht es? Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
+
Worum geht es? Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
Voraussetzungen:Kapitel 1 (die vier Bausteine eines Modells). Kein Solverwissen nötig; das ganze Kapitel kommt mit einer einzigen linprog-Zeile aus.
Danach können Sie: eine Anforderung daraufhin abklopfen, was sie nicht sagt; die Zielgröße so wählen, dass sie keinen Fehlanreiz setzt; harte von weichen Regeln unterscheiden — und begründen, warum diese Unterscheidung ein Jahr später über die Betriebsfähigkeit entscheidet.
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}# Auftrag: A B C D Estunden = np.array([ 60, 30, 30, 25, 15], dtype=float)
@@ -7074,7 +7074,7 @@ Deckungsbeitrag maximieren -> BD Umsatz 40,000 DB 19,000
🎯 Merksatz Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.
-
Warum funktioniert das? Weil eine Zielfunktion eine vollständige Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern wertlos. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
+
Warum funktioniert das? Weil eine Zielfunktion eine vollständige Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern wertlos. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
4.2 Lernziele
Nach diesem Kapitel können Sie …
@@ -7562,7 +7562,7 @@ Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:
Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.
Teil 3: Hart oder weich
-
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
+
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
Vier Kapitel, eine Frage: Was muss vorliegen, bevor ein Solver überhaupt sinnvoll gestartet werden kann? Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
+
Vier Kapitel, eine Frage: Was muss vorliegen, bevor ein Solver überhaupt sinnvoll gestartet werden kann? Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS-Symbolkonflikt zwischen ortools und highspy umgehen
+
Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS-Symbolkonflikt zwischen ortools und highspy umgehen
Wie wird aus einem Satz ein Modell?
@@ -7935,16 +7935,16 @@ Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt
Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil II)
-
🎯 Merksatz zum Diagramm Die erste Frage ist die wichtigste — und sie lautet nicht „welcher Solver ist der schnellste?“, sondern „welche Struktur hat mein Problem?“. Ein Zuordnungsproblem braucht keinen MILP-Solver, sondern eine Zeile linear_sum_assignment. Ein Dienstplan gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
+
🎯 Merksatz zum Diagramm Die erste Frage ist die wichtigste — und sie lautet nicht „welcher Solver ist der schnellste?“, sondern „welche Struktur hat mein Problem?“. Ein Zuordnungsproblem braucht keinen MILP-Solver, sondern eine Zeile linear_sum_assignment. Ein Dienstplan gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
-
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist Teil III der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
+
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist Teil III der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise
📌 Kapitel auf einen Blick
-
Worum geht es? Um das Kernverfahren des Operations Research. Wir bauen den Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher Schattenpreise kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
Danach können Sie: Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein Schattenpreis eine Entscheidung nicht tragen darf.
+
Worum geht es? Um das Kernverfahren des Operations Research. Wir bauen den Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher Schattenpreise kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
Danach können Sie: Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein Schattenpreis eine Entscheidung nicht tragen darf.
Zeitbedarf: ca. 7 Stunden — das umfangreichste Kapitel von Teil II.
Der Betriebsrat hat 20 Überstunden genehmigt. In welchem Bereich sollen sie eingesetzt werden?
-
from scipy.optimize import linprog
+
from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}# Deckungsbeitrag je Stueck (negiert, weil linprog minimiert)c = [-90, -140]
@@ -8022,9 +8022,9 @@ Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt
… eine Simplex-Iteration von Hand rechnen: Pivotspalte, Pivotzeile, Basistausch.
… erklären, warum das Verfahren terminiert und warum es das Optimum findet.
… das duale Problem zu einem gegebenen primalen aufstellen.
-
… Schattenpreise korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle, die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
+
… Schattenpreise korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle, die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
… aus einer Sensitivitätsanalyse eine begründete Kaufentscheidung ableiten.
-
… ein Modell auf Entartung prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
+
… ein Modell auf Entartung prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
… erklären, warum Zulässigkeit immer gegen eine Toleranz und nie mit == 0 geprüft wird.
Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus Kapitel 2: Das Optimum liegt in einer Ecke. Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
-Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient \mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py.
+Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient\mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py.
Was Sie in der Abbildung sehen. Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
Der Ablauf
@@ -8506,7 +8506,7 @@ Optimum nach 2 Pivotschritten erreicht.
Negative rechte Seite.-3x_1 - 2x_2 \le -12 (aus einer „\ge“-Bedingung entstanden) macht den Ursprung unzulässig. Dann braucht man Phase 1 mit künstlichen Variablen. Dieser Solver meldet das jetzt ehrlich.
Zyklen bei Entartung. Wenn mehrere Quotienten gleich sind, kann der Simplex theoretisch endlos kreisen. In der Praxis extrem selten; Bland’s Regel verhindert es beweisbar.
-
Den Simplex selbst produktiv einsetzen. Diese Implementierung ist ein Lehrmodell. HiGHS ist um Größenordnungen schneller und numerisch stabiler.
+
Den Simplex selbst produktiv einsetzen. Diese Implementierung ist ein Lehrmodell. HiGHS ist um Größenordnungen schneller und numerisch stabiler.
schattenpreise = -ergebnis.ineqlin.marginals — die eigentliche Korrektur.
Numerische Gegenprobe: Das Modell wird mit b_3 + 1 neu gelöst; der gemessene Zuwachs muss dem Schattenpreis entsprechen. Diese Prüfung ist unabhängig von jeder Vorzeichenkonvention und deshalb der zuverlässigste Test überhaupt.
-
Komplementärer Schlupf:s_i \cdot y_i = 0 muss für alle i gelten.
Übernehmen Sie dieses Muster in eigene Projekte. Der Aufwand ist eine Handvoll Zeilen, der Nutzen ist die Gewissheit, dass Ihre Handlungsempfehlung nicht das Gegenteil des Richtigen sagt.
@@ -9354,7 +9354,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
Aufgabe 5.1 ⭐ — Schlupf deuten. Ein LP liefert s = (0;\ 12{,}5;\ 0;\ 3) und y = (4{,}2;\ 0;\ 9{,}8;\ 0). (a) Welche Ressourcen sind Engpässe? (b) Ist die Lösung mit dem Satz vom komplementären Schlupf verträglich? (c) In welche Ressource würden Sie zuerst investieren?
Aufgabe 5.2 ⭐ — Vorzeichen prüfen. Ein Kollege maximiert Gewinn mit linprog und meldet: „Der Schattenpreis der Engpassmaschine ist -45 €.“ Was ist passiert, und wie lautet der korrekte Wert?
Aufgabe 5.3 ⭐⭐ — Simplex von Hand. Lösen Sie mit dem Tableau-Verfahren vollständig von Hand: \max\ 5x_1 + 4x_2 \quad \text{u. d. N.}\quad 6x_1+4x_2 \le 24,\quad x_1+2x_2 \le 6,\quad x_1,x_2\ge0 Geben Sie jedes Zwischentableau an und lesen Sie am Ende Lösung und Schattenpreise ab. Prüfen Sie mit scipy.optimize.linprog.
-
Aufgabe 5.4 ⭐⭐ — Duales Problem aufstellen. Stellen Sie zum Modell aus der Aufgabe Simplex von Hand das duale Problem auf, lösen Sie es mit linprog und weisen Sie den starken Dualitätssatz numerisch nach.
+
Aufgabe 5.4 ⭐⭐ — Duales Problem aufstellen. Stellen Sie zum Modell aus der Aufgabe Simplex von Hand das duale Problem auf, lösen Sie es mit linprog und weisen Sie den starken Dualitätssatz numerisch nach.
Aufgabe 5.5 ⭐⭐ — Unbeschränktheit erkennen. Was liefert SimplexTableauSolver für \max x_1 + x_2 u. d. N. x_1 - x_2 \le 5, x_1, x_2 \ge 0? Erklären Sie die Fehlermeldung geometrisch.
Aufgabe 5.6 ⭐⭐⭐ — Gültigkeitsbereich des Schattenpreises. Erweitern Sie Sensitivitaetsanalyse.py: Lösen Sie das Modell für Prüfkapazitäten von 30 bis 120 Stunden (Schrittweite 5) und tragen Sie Gewinn und Schattenpreis gegen die Kapazität auf. (a) Ab welcher Kapazität fällt der Schattenpreis auf einen niedrigeren Wert? Warum? (b) Wie viele Stunden sollte der Betrieb bei einem Angebotspreis von 18 €/h maximal zukaufen? (c) Zeichnen Sie den Gewinnverlauf. Was für eine Kurvenform ergibt sich, und warum?
Aufgabe 5.7 ⭐⭐⭐ — Phase 1 ergänzen. Erweitern Sie den SimplexTableauSolver um eine Phase-1-Rechnung mit künstlichen Variablen, sodass auch b_i < 0 verarbeitet werden kann. Testen Sie an: \max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,\quad x_1,x_2\ge0
@@ -9410,14 +9410,14 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
Schattenpreise beantworten die Managementfrage schlechthin: Wo ist der Engpass, und was ist seine Beseitigung wert?
Vorzeichen prüfen! Nach einer Negation der Zielfunktion müssen die Dualwerte zurücknegiert werden. Die numerische Gegenprobe („Kapazität um 1 erhöhen und neu lösen“) ist die sicherste Kontrolle.
-
Ausblick.Kapitel 6 bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
+
Ausblick.Kapitel 6 bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound
📌 Kapitel auf einen Blick
Worum geht es? Um Entscheidungen, die nicht teilbar sind: ganze Maschinen, Ja/Nein, „höchstens fünf davon“. Wir zeigen mit einem konkreten Gegenbeispiel, warum Runden falsch ist, und lernen die Modellierungstricks, mit denen sich logische Regeln in lineare Ungleichungen übersetzen lassen.
Danach können Sie: Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
Danach können Sie: Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
🎯 Merksatz Die LP-Relaxation ist keine ungefähre Antwort, die man nur noch glattziehen muss. Sie ist eine Antwort auf eine andere Frage — nämlich die, bei der man Lager auch zu 14,3 % eröffnen darf. Ihr Wert liegt nicht in ihrer Lösung, sondern in ihrer Schranke: Weniger als 724,14 € kann die richtige Antwort nicht kosten.
-
Genau diese Schranke ist der Hebel, mit dem Branch-and-Bound arbeitet. Wie, zeigt der Abschnitt Abschnitt 6.4.
Der verlockende Gedanke lautet: „Wir lösen das Problem kontinuierlich und runden.“ Eine verbreitete, aber unbelegte Behauptung dazu lautet, man verliere dadurch „oft 20–50 % des Gewinns“. Rechnen wir es nach, statt es zu behaupten.
-Abb. 6.1: Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur Restriktion, also ist die ganze Kante optimal. Aufrunden auf (1;1) führt aus dem zulässigen Bereich heraus, Abrunden auf (0;0) auf den Wert null. Rechts die Messung aus Runden_Gegenbeispiel.py über je 200 Zufallsinstanzen. Erzeugt von bilder_04/erzeuge_runden.py.
+Abb. 6.1: Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur Restriktion, also ist die ganze Kante optimal. Aufrunden auf (1;1) führt aus dem zulässigen Bereich heraus, Abrunden auf (0;0) auf den Wert null. Rechts die Messung aus Runden_Gegenbeispiel.py über je 200 Zufallsinstanzen. Erzeugt von bilder_04/erzeuge_runden.py.
✏️ Handrechnung 6.1: Ein Gegenbeispiel, das Sie im Kopf prüfen können
@@ -9675,7 +9675,7 @@ Loesung eine Nebenbedingung verletzt. 'Verlust' = Abstand zum exakten Op
6.4 Branch-and-Bound
-
MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen.
+
MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen.
Abb. 6.2: Der Suchbaum der Handrechnung weiter unten — nicht gezeichnet, sondern gerechnet: Das Skript löst die LP-Relaxationen und kappt nach denselben drei Regeln, die oben stehen. Ast B wird verworfen, weil sein Wert 18 den Incumbent 20 aus Ast A nicht schlägt. Erzeugt von bilder_04/erzeuge_branch_and_bound.py.
@@ -9885,7 +9885,7 @@ Genau hier wuerde Branch-and-Bound verzweigen:
Szenario. Ein Investor verteilt 100\,000\,\text{€} auf sechs Anlageklassen.
Jede Transaktion kostet 50 € Fixgebühr, unabhängig vom Betrag.
-
Höchstens 3 Positionen gleichzeitig (Kardinalitätsbeschränkung).
Wird eine Position eröffnet, dann mit mindestens 10 000 € (Mindestordergröße).
Höchstens 40 000 € pro Position.
@@ -10181,7 +10181,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
status = status_von_scipy(ergebnis)
-
übersetzt den SciPy-Rückgabewert in die gemeinsame Sprache aus Abschnitt 22.6 — die drei if-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt
+
übersetzt den SciPy-Rückgabewert in die gemeinsame Sprache aus Abschnitt 22.6 — die drei if-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt
schranke=-ergebnis.mip_dual_bound
@@ -10193,7 +10193,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
-
Warum das Ergebnis wirtschaftlich Sinn ergibt: Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
+
Warum das Ergebnis wirtschaftlich Sinn ergibt: Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
6.8 Wenn der Solver nicht fertig wird: Gap, Zeitlimit und Warm-Start
@@ -10747,7 +10747,7 @@ Instanz Variante Makespan Zeit Faktor
if status == OPTIMAL: ... else: return None. Wirft eine Lösung weg, die 9 % vom Optimum entfernt und damit vollkommen brauchbar ist. Werten Sie den Gap aus, nicht nur den Status.
Zeitlimit ohne Gap-Auswertung. Ein Limit erzwingt ein Ende, sagt aber nichts über die Qualität. Ohne den Gap wissen Sie nicht, ob Sie 0,1 % oder 60 % danebenliegen.
Warm-Start als Selbstverständlichkeit. Messen Sie ihn — auf Ihrer Instanzklasse, gegen den kalten Lauf. In der Hälfte der Fälle bringt er nichts.
-
Einen Hinweis für eine unzulässige Lösung geben. CP-SAT und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
+
Einen Hinweis für eine unzulässige Lösung geben.CP-SAT und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
@@ -11087,7 +11087,7 @@ funktioniert immer.
Runden ist keine Lösung: Aufrunden ist fast immer unzulässig (98–100 % der Fälle), Abrunden verliert im Mittel 10–26 %, im Extremfall alles.
Branch-and-Bound löst LP-Relaxationen, verzweigt an gebrochenen Variablen und kappt Äste anhand von Schranken. Der Preis: NP-Schwere.
Fünf Modellierungsmuster decken die meisten Praxisfälle ab: Fixkosten, Entweder-Oder, Wenn-Dann, Kardinalität, semikontinuierlich.
-
M so klein wie möglich — nicht nur wegen der Laufzeit. Ein übergroßes M kann Binärvariablen bei 10^{-8} hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (Trickle Flow).
+
M so klein wie möglich — nicht nur wegen der Laufzeit. Ein übergroßes M kann Binärvariablen bei 10^{-8} hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (Trickle Flow).
OPTIMAL ist nicht der Normalfall. Werten Sie jeden Status aus und lesen Sie den MIP-Gap — er ist eine Garantie („höchstens 2,3 % vom Optimum“), keine Schätzung. Der Beweis der Optimalität kostet meist ein Vielfaches dessen, was das Finden der optimalen Lösung kostet.
Warm-Starts können nichts verfälschen, aber auch nichts versprechen. Ob ein Startwert hilft, hängt davon ab, ob er besser ist als das, was der Solver in derselben Zeit von allein findet — im Buch gemessen: einmal Faktor 2,2, einmal gar nichts.
Prüfen Sie jede modellierte Regel nach dem Lösen mit einer assert-Zeile — und rechnen Sie den Zielwert aus der Lösung nach, statt ihm zu glauben.
@@ -11099,7 +11099,7 @@ funktioniert immer.
📌 Kapitel auf einen Blick
Worum geht es? Um ein völlig anderes Denkmodell: Statt algebraischer Ungleichungen arbeitet Constraint Programming mit Wertebereichen und logischen Regeln. Für Dienstpläne, Zuordnungen und Reihenfolgen ist das die mit Abstand produktivste Methode.
Danach können Sie: Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und alle fünf Solver-Antworten auseinanderhalten, statt nur auf OPTIMAL zu prüfen.
+
Danach können Sie: Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und alle fünf Solver-Antworten auseinanderhalten, statt nur auf OPTIMAL zu prüfen.
Zeitbedarf: ca. 6,5 Stunden.
Programme: Propagation_Demo.py
@@ -11142,7 +11142,7 @@ Mi -> Abel
Do -> Bux
Fr -> Abel
-
Elf Zeilen Modell, und der Plan steht. Beachten Sie besonders die Zeile m.AddExactlyOne(...). Sie sagt wörtlich, was gemeint ist: genau eine Person je Tag. In MILP hätte man dafür eine Gleichung \sum_p x_{p,d} = 1 aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in CP-SAT bleibt er eine Zeile.
+
Elf Zeilen Modell, und der Plan steht. Beachten Sie besonders die Zeile m.AddExactlyOne(...). Sie sagt wörtlich, was gemeint ist: genau eine Person je Tag. In MILP hätte man dafür eine Gleichung \sum_p x_{p,d} = 1 aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in CP-SAT bleibt er eine Zeile.
Und jetzt sehen Sie sich das Ergebnis genau an.Cor bekommt keinen einzigen Dienst. Dane arbeitet Montag und Dienstag hintereinander. Ist das ein Fehler?
Nein — und das ist die wichtigste Lektion dieses Kapitels. Der Plan erfüllt jede Regel, die wir aufgeschrieben haben. Wir haben nur nie nach Fairness gefragt, und wir haben nie gesagt, dass Dienste nicht aufeinanderfolgen sollen. Ein Solver liefert exakt das Bestellte — nicht das Gemeinte.
@@ -11153,17 +11153,17 @@ Fr -> Abel
Nach diesem Kapitel können Sie …
… erklären, wodurch sich Constraint Programming von MILP unterscheidet.
-
… die Wirkung von Constraint Propagation an einem Beispiel nachvollziehen.
… die wichtigsten globalen Constraints einsetzen: AddExactlyOne, AddAllDifferent, AddNoOverlap, AddCumulative.
… mit Intervallvariablen ein Maschinenbelegungsproblem modellieren.
… harte Regeln und weiche Ziele in einem Modell kombinieren und gewichten.
… die fünf CP-SAT-Statusfälle unterscheiden — insbesondere INFEASIBLE (es gibt keine Lösung) von UNKNOWN (es wurde keine gefunden) und MODEL_INVALID (Ihr Code ist fehlerhaft).
… begründen, warum num_workers = 1 für einen reproduzierbaren Lauf nötig ist und ein fester random_seed allein nicht genügt — und was ein Test deshalb prüfen darf.
-
… begründen, warum ein Strafgewicht ein Wechselkurs ist, und welche Regeln deshalb niemals in die Zielfunktion gehören.
+
… begründen, warum ein Strafgewicht ein Wechselkurs ist, und welche Regeln deshalb niemals in die Zielfunktion gehören.
7.3 Ein anderes Denkmodell
-
Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:
+
Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:
Der CP-SAT-Solver von Google OR-Tools verbindet Constraint Programming mit modernen SAT-Techniken (Boolean Satisfiability, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere CDCL (Conflict-Driven Clause Learning, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und Job-Shop-Scheduling.
+
Der CP-SAT-Solver von Google OR-Tools verbindet Constraint Programming mit modernen SAT-Techniken (Boolean Satisfiability, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere CDCL (Conflict-Driven Clause Learning, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und Job-Shop-Scheduling.
🔤 Formel-Übersetzer: was ein globales Constraint zusichert
-
Globale Constraints tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
+
Globale Constraints tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
@@ -11413,7 +11413,7 @@ Fr -> Abel
Der Unterschied zu MILP in einem Satz: Dort formuliert man Ungleichungen, aus denen die Regel folgt; hier schreibt man die Regel selbst hin.
-
💡 Warum globale Constraints so viel bringenAddAllDifferent über 9 Variablen ersetzt \binom{9}{2} = 36 paarweise Ungleichheitsbedingungen — und propagiert stärker als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten Graphen). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
+
💡 Warum globale Constraints so viel bringenAddAllDifferent über 9 Variablen ersetzt \binom{9}{2} = 36 paarweise Ungleichheitsbedingungen — und propagiert stärker als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten Graphen). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
koppeln die Hilfsvariable lochin beide Richtungen an das Muster „arbeitet–frei–arbeitet“. Die erste Zeile erzwingt: wennloch = 1, dann liegt das Muster vor. Die zweite: wennloch = 0, dann liegt es nicht vor. In diesem Modell genügte streng genommen die erste Zeile, weil loch nur mit positiven Strafkosten vorkommt und der Minimierer es daher von selbst auf 0 drückt — die zweite Zeile macht das Modell aber robust gegen spätere Änderungen und ist selbstdokumentierend.
2. Fairness über Spannweite (W3).AddMaxEquality und AddMinEquality sind CP-SAT-Konstrukte, die in MILP mehrere Big-M-Ungleichungen bräuchten. Die Spannweite \max - \min zu minimieren ist ein Standardrezept für „gleichmäßig verteilen“.
-
3. Kostenzerlegung. Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: „Warum bekomme ich die Stunde?“ Wer darauf keine Antwort hat, dessen System wird nicht benutzt (Kapitel 22, Explainable OR).
+
3. Kostenzerlegung. Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: „Warum bekomme ich die Stunde?“ Wer darauf keine Antwort hat, dessen System wird nicht benutzt (Kapitel 22, Explainable OR).
4. Statusauswertung ohne CP-SAT-Vokabular. Nach Solve() steht nur eine einzige Zeile mit einer CP-SAT-Konstante darin:
status = status_von_cpsat(rohstatus)
-
Danach heißen die Fälle status.modellfehler (die Regeln widersprechen sich — das Modell muss geändert werden) und status.brauchbar (es liegt etwas Ausführbares vor). Das ist dieselbe Fallunterscheidung wie bei HiGHS in Kapitel 6 und bei GLOP in Kapitel 5; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen cp_model.INFEASIBLE schreibt, kann ihn bei einem Solverwechsel wegwerfen — Abschnitt 22.7 führt genau diesen Wechsel vor.
+
Danach heißen die Fälle status.modellfehler (die Regeln widersprechen sich — das Modell muss geändert werden) und status.brauchbar (es liegt etwas Ausführbares vor). Das ist dieselbe Fallunterscheidung wie bei HiGHS in Kapitel 6 und bei GLOP in Kapitel 5; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen cp_model.INFEASIBLE schreibt, kann ihn bei einem Solverwechsel wegwerfen — Abschnitt 22.7 führt genau diesen Wechsel vor.
Die Königsdisziplin von CP-SAT sind Zeitplanungsprobleme. Intervallvariablen sind dabei das zentrale Werkzeug.
-
Problem. Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (Makespan).
+
Problem. Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (Makespan).
#!/usr/bin/env python3# JobShop_Intervalle.py
@@ -12141,12 +12141,12 @@ random_seed = 1 in JEDEM Lauf, 4 Wiederholungen je Arbeiterzahl.
deshalb eine Entscheidung fuer den Test, nicht fuer den Betrieb.
==============================================================================
Was die Messung zeigt
-
Erstens: Die Beschleunigung ist überlinear. Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern gut zwölfmal schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt nicht achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern verschiedene Strategien nebeneinander — Suche mit LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
+
Erstens: Die Beschleunigung ist überlinear. Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern gut zwölfmal schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt nicht achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern verschiedene Strategien nebeneinander — Suche mit LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
Daraus folgt zweierlei. Ein Speedup über der Kernzahl ist bei CP-SAT normal und kein Grund zum Misstrauen. Und der Zugewinn flacht ab: Zwischen vier und acht Arbeitern lag in unseren Wiederholungen mal das eine, mal das andere vorn — ab einer gewissen Zahl kosten Abstimmung und Speicherbandbreite mehr, als ein weiterer Suchstrang einbringt. Auch hier gilt: messen, nicht schätzen.
Zweitens — und das ist der Befund, der die Testpraxis bestimmt: Der Seed genügt nicht. Bei einem Arbeiter liefern vier Läufe einen Plan. Schon bei zwei Arbeitern liefern vier Läufe drei verschiedene — bei identischem random_seed und identischem Zielwert 183.
Es braucht also keine acht Arbeiter, um die Reproduzierbarkeit zu verlieren. Zwei genügen. Der Grund ist einfach, sobald man ihn ausspricht: Der Seed legt fest, wie ein einzelner Suchstrang würfelt. Er legt nicht fest, welcher von mehreren zuerst fertig wird — das entscheidet die Uhr, und die ist bei jedem Lauf anders.
-
⚠️ random_seed allein macht nichts reproduzierbar Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen parallelen Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln. Reproduzierbar wird der Lauf erst mit num_workers = 1und Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
+
⚠️ random_seed allein macht nichts reproduzierbar Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen parallelen Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln. Reproduzierbar wird der Lauf erst mit num_workers = 1und Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
Was daraus für Tests folgt
Der Zielwert war in allen sechzehn Läufen derselbe: 183. Die Gestalt der Lösung war es nicht. Genau daran entscheidet sich, was ein Test prüfen darf:
@@ -12563,7 +12563,7 @@ genau die Information, die zur Fehlersuche noetig waere.
Aufgabe 7.1 ⭐ — Propagation nachvollziehen.x_1 \in \{1,\dots,6\}, x_2 \in \{1,\dots,6\}, Bedingungen: x_1 < x_2 und x_1 + x_2 = 8. Reduzieren Sie beide Wertebereiche von Hand so weit wie möglich.
Aufgabe 7.2 ⭐ — Hart oder weich? Klassifizieren Sie und begründen Sie, welche Strafhöhe Sie ansetzen würden: (a) Gesetzliche Mindestruhezeit. (b) „Herr Bauer möchte montags frei.“ (c) „In jeder Schicht mindestens eine examinierte Kraft.“ (d) „Möglichst keine geteilten Dienste.“ (e) „Kein Dienst länger als 10 Stunden.“
Aufgabe 7.3 ⭐⭐ — Regeln ergänzen. Erweitern Sie das Vertretungssystem um: (a) Frau Albrecht ist ab Slot 3 nicht mehr verfügbar. (b) Herr Bauer und Frau Koch sollen nicht beide am selben Tag eingesetzt werden. (c) Wer Slot 1 übernimmt, soll bevorzugt auch Slot 2 bekommen (Belohnung statt Strafe).
-
Aufgabe 7.4 ⭐⭐ — Infeasibility erzeugen und diagnostizieren. Setzen Sie MAX_VERTRETUNGEN = 0. Was meldet das Programm? Bauen Sie anschließend eine Schlupfvariable je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
+
Aufgabe 7.4 ⭐⭐ — Infeasibility erzeugen und diagnostizieren. Setzen Sie MAX_VERTRETUNGEN = 0. Was meldet das Programm? Bauen Sie anschließend eine Schlupfvariable je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
Aufgabe 7.5 ⭐⭐ — Sudoku. Lösen Sie ein 9×9-Sudoku mit CP-SAT. Sie brauchen nur NewIntVar(1, 9, ...) und AddAllDifferent für Zeilen, Spalten und die neun 3×3-Blöcke. Wie viele Zeilen Modellcode benötigen Sie?
Aufgabe 7.6 ⭐⭐ — Die Grenze der Parallelität finden.Parallele_Suche.py misst 1, 2, 4 und 8 Arbeiter. (a) Ergänzen Sie 16 und 24 (bzw. die Kernzahl Ihres Rechners). Ab wo bringt ein weiterer Arbeiter nichts mehr — und wird es irgendwann wieder schlechter? (b) Setzen Sie random_seed bei jedem Lauf auf einen anderen Wert, aber num_workers = 1. Wie viele verschiedene Pläne kommen jetzt heraus? Was folgt daraus für die Frage, welcher der beiden Parameter die Reproduzierbarkeit sichert? (c) Vergrößern Sie die Instanz auf 15 Aufträge. Ab welcher Größe erreicht der Lauf mit einem Arbeiter das Zeitlimit, während acht noch OPTIMAL melden?
Aufgabe 7.7 ⭐⭐⭐ — Job-Shop erweitern. Ergänzen Sie JobShop_Intervalle.py um: (a) Rüstzeiten von 1 Zeiteinheit zwischen zwei Aufträgen auf derselben Maschine. (b) Fälligkeitstermine je Auftrag mit Strafkosten für Verspätung. (c) Eine zweite Fräse (Kapazität 2 statt 1) mit AddCumulative.
@@ -12762,7 +12762,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
Gewichte nach Bauchgefühl setzen. Rechnen Sie stattdessen die Tabelle wie oben und lassen Sie den Auftraggeber die Zeile aussuchen. Das dauert eine Stunde und ersetzt drei Abstimmungsrunden.
Eine unverhandelbare Regel weich modellieren. Alles, was einen Preis hat, wird irgendwann gekauft. Gesetzliche Ruhezeiten, Qualifikationsanforderungen und Betriebsvereinbarungen gehören unter die harten Nebenbedingungen — auch wenn das Modell dann INFEASIBLE melden kann. Dieses INFEASIBLE ist die ehrliche Antwort: Der Plan ist mit dem vorhandenen Personal nicht zulässig zu machen.
-
Gewichte in verschiedenen Einheiten mischen. Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren Konditionszahl obendrein leidet (Abschnitt 2.7).
+
Gewichte in verschiedenen Einheiten mischen. Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren Konditionszahl obendrein leidet (Abschnitt 2.7).
Nur ein Gewicht ausprobieren. Ein einziger Lauf sagt nichts darüber, ob Sie nahe an einem Kipppunkt stehen. Zwischen den Gewichten 5 und 10 ändert sich der Plan oben erheblich.
@@ -12793,7 +12793,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
7.13 Zusammenfassung
CP denkt in Wertebereichen und Regeln, nicht in Ungleichungen und Schranken.
-
Propagation streicht unmögliche Werte, bevor gesucht wird; Conflict Learning verhindert, dieselbe Sackgasse zweimal zu betreten.
+
Propagation streicht unmögliche Werte, bevor gesucht wird; Conflict Learning verhindert, dieselbe Sackgasse zweimal zu betreten.
Globale Constraints wie , und ersetzen ganze Blöcke von Big-M-Ungleichungen und propagieren stärker.
Intervallvariablen sind das Werkzeug für Zeitplanung; AddNoOverlap erledigt in einer Zeile, was in MILP dutzende Disjunktionen kostet.
Weiche Ziele mit Strafkosten machen Modelle robust gegen INFEASIBLE — und die Kostenzerlegung macht sie erklärbar.
@@ -12885,7 +12885,7 @@ Diaz -> Ost (24 min)
Engel -> West (44 min)
Gesamtfahrzeit: 118 min
-
Eine Zeile Code, und das Problem ist beweisbar optimal gelöst — linear_sum_assignment ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden Solver auskommt.
+
Eine Zeile Code, und das Problem ist beweisbar optimal gelöst — linear_sum_assignment ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden Solver auskommt.
Interessanter ist aber, was der Algorithmus nicht tut. Der kleinste Wert der ganzen Tabelle ist die 18 bei Cakir → Nord. Die naheliegende Vorgehensweise — „nimm immer das günstigste noch freie Paar“ — beginnt also genau dort:
@@ -12917,7 +12917,7 @@ Gesamtfahrzeit: 118 min
🎯 Merksatz Der beste erste Zug ist selten Teil der besten Gesamtlösung. Genau deshalb gibt es Operations Research: Optimierung heißt, Entscheidungen gemeinsam zu treffen statt nacheinander. Bei vier Monteuren kostet die gierige Regel 8 %; bei vierzig kostet sie regelmäßig ein Vielfaches.
-
Warum funktioniert das? Weil das Zuordnungsproblem eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist total unimodular. Das bedeutet — wir kommen in Abschnitt 8.4 darauf zurück —, dass die LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
+
Warum funktioniert das? Weil das Zuordnungsproblem eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist total unimodular. Das bedeutet — wir kommen in Abschnitt 8.4 darauf zurück —, dass die LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
8.2 Lernziele
Nach diesem Kapitel können Sie …
@@ -12925,14 +12925,14 @@ Gesamtfahrzeit: 118 min
… ein Transportproblem als Graph mit Quellen, Senken und Kapazitäten modellieren.
… den Flusserhaltungssatz aufstellen und seine Bedeutung erklären.
… begründen, warum Zuordnungsprobleme ohne Ganzzahligkeitsbedingung ganzzahlig lösbar sind (totale Unimodularität).
-
… ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
-
… einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
+
… ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
+
… einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
… begründen, warum eine gierige Zuordnung systematisch schlechter ist als eine gemeinsame Optimierung.
… einen Tourenplan gegen die Wirklichkeit prüfen — unabhängig von den Bausteinen, aus denen das Modell gebaut wurde.
8.3 Graphen als Modellsprache
-
Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern GraphenG = (V, E):
+
Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern GraphenG = (V, E):
V — die Knoten (vertices): Server, Depots, Kunden, Konten, Lager.
E — die gerichteten Kanten (edges): Datenleitungen, Straßen, Überweisungswege.
@@ -12941,7 +12941,7 @@ Gesamtfahrzeit: 118 min
Minimum-Cost Network Flow Problem — deutsch: kostenminimales Flussproblem. Es ist das mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverteilung.
-Abb. 8.1: Das Netzwerk aus Min_Cost_Flow.py samt Lösung: 140 € für 30 Einheiten. Die billige Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 € bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die Dualwerte der Flusserhaltung. Erzeugt von bilder_04/erzeuge_min_cost_flow.py.
+Abb. 8.1: Das Netzwerk aus Min_Cost_Flow.py samt Lösung: 140 € für 30 Einheiten. Die billige Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 € bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die Dualwerte der Flusserhaltung. Erzeugt von bilder_04/erzeuge_min_cost_flow.py.
Sei x_{ij} \ge 0 der Fluss über Kante (i,j), c_{ij} die Kosten je Einheit und u_{ij} die Kapazität:
Satz. Die Extrempunkte der Menge aller doppelt-stochastischen Matrizen (alle Zeilensummen = 1, alle Spaltensummen = 1, x_{ij} \ge 0) sind genau die Permutationsmatrizen (alle x_{ij} \in \{0,1\}).
-
Warum das praktisch enorm wichtig ist: Nach dem Fundamentalsatz aus Kapitel 2 liegt das LP-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
+
Warum das praktisch enorm wichtig ist: Nach dem Fundamentalsatz aus Kapitel 2 liegt das LP-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
🎯 Merksatz Beim Zuordnungsproblem müssen Sie die Ganzzahligkeit nicht fordern — ein gewöhnlicher LP-Solver liefert von selbst eine 0/1-Lösung. Sie sparen sich damit die NP-Schwere von Branch-and-Bound vollständig.
Die praktische Folge in einem Satz:Bei Zuordnungs- und Flussproblemen dürfen Sie die Ganzzahligkeit weglassen und trotzdem ganzzahlige Lösungen erwarten — Sie sparen sich die NP-Schwere von Branch-and-Bound vollständig.
-
Und die Warnung dazu: Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität. Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
+
Und die Warnung dazu: Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität. Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Time Windows (CVRPTW) erweitert es auf eine Flotte mit Kapazitätsgrenzen und Kundenzeitfenstern [e_i, l_i].
+
Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Time Windows (CVRPTW) erweitert es auf eine Flotte mit Kapazitätsgrenzen und Kundenzeitfenstern [e_i, l_i].
Abb. 8.2: Die Lösung der Instanz aus VRP_Flotten_Routing.py: 16 Kunden, vier Fahrzeuge, 619 km. Beachten Sie, dass sich die Touren kreuzen. Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von bilder_04/erzeuge_vrp_touren.py.
@@ -13282,7 +13282,7 @@ Umschlag -> Kunde_2 5.0 10 3.00 15.00
Was Sie auf der Karte sehen — und was nicht. Die Instanz ist synthetisch: 17 zufällige Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie lagegetreu auf eine reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände zwischen den Punkten bis auf 0,001 % erhält. Die Karte zeigt also dieselbe Instanz, mit der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein Wert in diesem Kapitel ändert sich dadurch.
Die Touren sind aus demselben Grund Luftlinien und keine Straßenrouten: Das Modell kennt keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — Abschnitt 8.7 sagt, was dabei zu beachten ist.
Kurzzyklen verhindern
-
Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt Rangvariablen u_i ein (die Position des Knotens in der Tour):
+
Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt Rangvariablen u_i ein (die Position des Knotens in der Tour):
Abb. 8.3: Links eine Lösung, die jede Gradbedingung erfüllt und trotzdem unbrauchbar ist; rechts dieselbe Instanz mit Kurzzyklus-Bedingungen. Erzeugt von bilder_04/erzeuge_vrp_subtours.py.
@@ -13296,7 +13296,7 @@ u_i - u_j + C \cdot x_{ij} \le C - d_j \qquad \forall i \ne j
Wird die Kante nicht benutzt (x_{ij} = 0), reduziert sich die Ungleichung auf u_i - u_j \le C - d_j, was durch hinreichend großes C immer erfüllt ist — das Big-M-Muster aus Kapitel 6.
-
💡 In der Praxis: nicht selbst modellieren Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie OR-Tools, spezialisierte Metaheuristiken. Nutzen Sie für Routing die Routing-Bibliothek, nicht ein selbstgebautes MILP.
+
💡 In der Praxis: nicht selbst modellieren Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie OR-Tools, spezialisierte Metaheuristiken. Nutzen Sie für Routing die Routing-Bibliothek, nicht ein selbstgebautes MILP.
Praxisbeispiel: Flotten-Routing
#!/usr/bin/env python3
@@ -13465,7 +13465,7 @@ u_i - u_j + C \cdot x_{ij} \le C - d_j \qquad \forall i \ne j
if__name__=="__main__": loese_cvrptw()
Min(CumulVar) mit „Ankunftszeit“ verwechseln. Der Solver liefert ein Intervall möglicher Zeiten. Min() ist die früheste, Max() die späteste zulässige Zeit — die tatsächliche Fahrt kann irgendwo dazwischen starten.
Servicezeit im Depot mitzählen. Beim Start am Depot fällt keine Servicezeit an, sonst verschiebt sich der ganze Plan.
@@ -13717,7 +13717,7 @@ plausibel aussieht.
Eine Dimension vergessen. Ladung, Lenkzeit, Kühlkette, Gewicht und Volumen — jede Größe, die begrenzt ist, braucht ihre eigene Dimension. Zählen Sie sie vor dem Modellieren auf einem Blatt Papier auf.
Die Prüfung aus denselben Bausteinen bauen wie das Modell. Wer die Ladung mit loesung.Value(ladungs_dimension.CumulVar(...)) prüft, fragt das Modell, ob es sich an sich selbst hält. Rechnen Sie stattdessen aus der ausgegebenen Tour neu nach.
-
Zwei Läufe nur an der Zielfunktion vergleichen. 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
+
Zwei Läufe nur an der Zielfunktion vergleichen. 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
Unbenutzte Fahrzeuge übersehen. Drei Fahrzeuge, die im Depot stehen, während eines alles fährt, sind fast immer ein Zeichen für eine fehlende Beschränkung.
@@ -13747,21 +13747,21 @@ plausibel aussieht.
Graphen sind die natürliche Sprache für Transport-, Zuordnungs- und Routenprobleme.
Flusserhaltung ist die Kirchhoff-Regel des Operations Research: Was hineingeht, kommt heraus — abzüglich des Knotensaldos.
-
Totale Unimodularität macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier integrality setzt, verschenkt Laufzeit ohne Gegenwert.
+
Totale Unimodularität macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier integrality setzt, verschenkt Laufzeit ohne Gegenwert.
Spezialisierte Algorithmen schlagen allgemeine Solver deutlich: Der Ungarische Algorithmus löst in O(n^3), wofür ein MILP-Solver Branch-and-Bound bräuchte.
Für Routing gilt: Nutzen Sie die Routing-Bibliothek. Metaheuristiken liefern in Sekunden sehr gute Touren; exakte Optimalität ist bei realistischen Größen unrealistisch und praktisch entbehrlich.
Die Routing-Bibliothek kennt nur Dimensionen. Ladung, Lenkzeit, Kühlkette, Gewicht — jede begrenzte Größe braucht ihre eigene. Was nicht als Dimension angelegt ist, wird nicht begrenzt, und der Solver meldet stolz eine kürzere Strecke.
Totale Unimodularität ist zerbrechlich. Eine einzige zusätzliche Bedingung, die nicht ins Schema passt — Kardinalität, Fixkosten, Mindestmenge —, zerstört sie. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
Der beste erste Zug ist selten Teil der besten Gesamtlösung. Gierige Regeln kosten schon bei vier Zuordnungen 8 %.
-
Ausblick.Teil III verlässt die lineare Welt. Kapitel 11 führt quadratische Zielfunktionen und die KKT-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
+
Ausblick.Teil III verlässt die lineare Welt. Kapitel 11 führt quadratische Zielfunktionen und die KKT-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt
📌 Kapitel auf einen Blick
-
Worum geht es? Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der Solver kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
Danach können Sie: eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
+
Worum geht es? Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der Solver kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
Danach können Sie: eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
Ohne Formel gesagt: „Sortiere die Aufträge so, dass möglichst wenig geputzt werden muss.“
-
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein asymmetrisches Rundreiseproblem (Kapitel 8) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
+
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein asymmetrisches Rundreiseproblem (Kapitel 8) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
📖 Definition: Metaheuristik
Ein Verfahrensrahmen, der eine vorhandene Lösung schrittweise verändert und dabei steuert, welche Veränderungen übernommen werden. „Meta“, weil der Rahmen nichts über das Problem weiß: Er braucht nur eine Startlösung, einen Zug und eine Bewertung. Dieselbe Mechanik läuft über Tourenplanung, Personaleinsatz und Portfolioauswahl.
Der Preis dafür steht in derselben Zeile: Ein Verfahren, das nichts über das Problem weiß, kann auch nichts über die Güte seines Ergebnisses sagen. Es liefert eine Lösung, keine Schranke.
Die Instanz
-
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
+
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
Eine Feinheit der Instanz ist wichtiger, als sie aussieht: Die Zahl der Produktfamilien wächst mit der Auftragszahl (eine Familie je zehn Aufträge). Bei fester Familienzahl würde das Problem mit wachsendem n nämlich leichter — der Plan bestünde irgendwann aus ein paar großen Blöcken, und jede Faustregel fände ihn. Beim ersten Entwurf dieses Kapitels ist mir genau das passiert: Die Metaheuristik verbesserte bei 60 Aufträgen um 30 %, bei 200 Aufträgen um 0 %. Nicht weil sie versagte, sondern weil es nichts mehr zu verbessern gab.
9.4 Lokale Suche: der Zug und seine Kosten
@@ -14699,7 +14699,7 @@ Und warum die Starttemperatur ausgemessen gehoert:
print("exakter Lauf dazu, wenn man am Ende die heuristische Loesung einsetzt:")print("nicht wegen seiner Loesung, sondern wegen seiner Schranke.")print("="*88)
-
Erwartete Ausgabe (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
+
Erwartete Ausgabe (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
========================================================================================
AB WANN LOHNT SICH DIE METAHEURISTIK?
========================================================================================
@@ -14753,7 +14753,7 @@ nicht wegen seiner Loesung, sondern wegen seiner Schranke.
60
-
Exakt. CP-SAT beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht.
+
Exakt.CP-SAT beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht.
200
@@ -14775,7 +14775,7 @@ nicht wegen seiner Loesung, sondern wegen seiner Schranke.
9.7 Large Neighborhood Search
Die Tabelle oben endet mit einem Widerspruch. Bei 500 Aufträgen scheitert der exakte Solver an der Größe; die Metaheuristik scheitert an der Kleinheit ihrer Züge — sie verschiebt jeweils einen einzigen Auftrag und kann eine ganze Passage nicht auf einmal umbauen.
-
Large Neighborhood Search (LNS) setzt beide ein, jeden für das, was er kann:
+
Large Neighborhood Search (LNS) setzt beide ein, jeden für das, was er kann:
@@ -15263,7 +15263,7 @@ Abstand zum Optimum.
Wenn es eine natürliche „Kreuzung“ zweier Lösungen gibt. Für Reihenfolgen ist genau das der schwierige Teil. Bibliothek: pymoo.
Bergsteigen, das zuletzt gemachte Züge eine Zeit lang verbietet.
Wenn die Suche zwischen zwei Zuständen pendelt. Oft mit weniger Kalibrierung als Annealing.
@@ -15319,7 +15319,7 @@ Abstand zum Optimum.
9.13 Zusammenfassung
Eine Faustregel ist selten dumm, aber immer kurzsichtig: Sie bewertet einen Schritt danach, was er kostet, nicht danach, was er übrig lässt.
-
Lokale Suche steht und fällt mit der Kostenänderung in O(1). Wer den Zug über die volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
+
Lokale Suche steht und fällt mit der Kostenänderung in O(1). Wer den Zug über die volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
Simulated Annealing nimmt Verschlechterungen mit e^{-\Delta/T} an. T und \Delta haben dieselbe Einheit — die Temperatur gehört an der Zuggröße kalibriert, nicht am Zielfunktionswert. Die verbreitete Regel „20 bis 50 % Annahmequote“ war auf dieser Aufgabe falsch.
Der schlechteste Zwischenwert ist die beste Diagnosezahl eines Annealing-Laufs: Er zeigt eine zu heiße Starttemperatur sofort, ohne dass man das Ergebnis abwarten muss.
Rechnen Sie immer das simple Bergsteigen mit. Hier lieferte es 93 % des Ertrags.
@@ -15331,7 +15331,7 @@ Abstand zum Optimum.
Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten
📌 Kapitel auf einen Blick
-
Worum geht es? Um die andere Antwort auf die Frage aus Kapitel 9: Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
+
Worum geht es? Um die andere Antwort auf die Frage aus Kapitel 9: Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
Voraussetzungen:Abschnitt 5.6 (Dualwerte) und Kapitel 6 (Rucksack.py). Beides wird gebraucht, aber nicht neu erklärt.
Danach können Sie: ein Problem in Master und Teilproblem zerlegen; die Spaltengenerierungsschleife selbst schreiben; und begründen, wann sich der Aufwand lohnt — die Antwort hängt an einer einzigen Kennzahl der Instanz.
Zeitbedarf: ca. 3 Stunden.
@@ -15347,7 +15347,7 @@ Abstand zum Optimum.
Die naheliegende Frage — „welches Stück kommt auf welche Rolle?“ — führt zu 35 Einzelentscheidungen. Die bessere Frage lautet: „Wie oft schneide ich welches Muster?“
Und jetzt der Punkt. Sehen Sie sich an, was da herauskommt: drei Schnittmuster. Kein Zuordnungsplan für 35 Stücke, sondern eine Anweisung, die an der Maschine hängen kann — neunmal dieses Muster, zweimal jenes, einmal das dritte.
Das ist kein kosmetischer Unterschied. Das Modell hat eine Variable je Muster statt einer je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschwindet ein Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.
-
🎯 Merksatz Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die Frage, worüber die Variablen laufen.
+
🎯 Merksatz Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die Frage, worüber die Variablen laufen.
Warum funktioniert das? Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei Breiten und einer 1 000-mm-Rolle sind es 16; bei dreizehn Breiten und 5 600 mm sind es zehntausende, und bei einer echten Papierfabrik mehr, als sich speichern lässt. Das ganze Kapitel handelt davon, wie man mit Mustern rechnet, ohne sie aufzuschreiben.
@@ -15385,7 +15385,7 @@ Abstand zum Optimum.
… erklären, warum das naheliegende Zuordnungsmodell an Symmetrie scheitert.
… ein Problem in Master und Pricing-Teilproblem zerlegen.
… das Abbruchkriterium über die reduzierten Kosten begründen.
… an einer Kennzahl der Instanz abschätzen, ob sich das Verfahren lohnt.
@@ -15394,7 +15394,7 @@ Abstand zum Optimum.
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable z_{jr} für „Stück j liegt auf Rolle r“, dazu y_r für „Rolle r wird benutzt”.
Zwei Dinge gehen dabei schief.
Die Größe. Bei 219 Zuschnitten und höchstens 82 Rollen sind das knapp 18 000 Binärvariablen — unangenehm, aber allein noch kein Hindernis.
-
Die Symmetrie. Das ist das eigentliche Problem. Alle Mutterrollen sind gleich. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht und arbeitet sie einzeln ab.
+
Die Symmetrie. Das ist das eigentliche Problem. Alle Mutterrollen sind gleich. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht und arbeitet sie einzeln ab.
⚠️ Woran man ein Symmetrieproblem erkennt
Der Suchbaum wächst, aber die Schranke bewegt sich nicht. Im Protokoll aus Abschnitt 6.8 sieht das so aus: Die Zahl der Knoten steigt in die Hunderttausende, der Incumbent verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
@@ -15452,7 +15452,7 @@ Abstand zum Optimum.
Fange mit wenigen Mustern an. Frage nach jeder Lösung, ob es ein noch nicht bekanntes Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.
-
Die Frage „lohnt sich noch ein Muster?” beantworten die Dualwerte des Master-LPs. Der Dualwert \pi_i zur Bedarfszeile i ist genau der Schattenpreis aus Abschnitt 5.6: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite i kostet.
+
Die Frage „lohnt sich noch ein Muster?” beantworten die Dualwerte des Master-LPs. Der Dualwert \pi_i zur Bedarfszeile i ist genau der Schattenpreis aus Abschnitt 5.6: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite i kostet.
Abb. 10.1: Der Kreislauf zwischen Master-LP und Pricing-Problem. Erzeugt von bilder_04/erzeuge_spaltengenerierung_flow.py.
@@ -15498,7 +15498,7 @@ Abstand zum Optimum.
Die letzte Zeile ist der Grund, warum das Verfahren exakt ist und nicht heuristisch: Wenn kein Muster mehr lohnende reduzierte Kosten hat, ist die LP-Lösung über allen Mustern optimal — auch über den nie erzeugten.
-
Und wie findet man das beste neue Muster? Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein Rucksackproblem:
+
Und wie findet man das beste neue Muster? Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein Rucksackproblem:
Runden. Die LP-Lösung ist keine Näherung der ganzzahligen Lösung — sie kann beliebig weit danebenliegen, und gerundet sogar unzulässig werden.
Ein zu großes Big-M. Es macht das Modell nicht falsch, sondern die Suche langsam und die Schranken wertlos. So klein wie zulässig, nie „sicherheitshalber groß”.
-
Zu früh heuristisch werden. Der Umschlagpunkt, ab dem eine Metaheuristik den exakten Solver schlägt, ist eine Eigenschaft des Problems und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
+
Zu früh heuristisch werden. Der Umschlagpunkt, ab dem eine Metaheuristik den exakten Solver schlägt, ist eine Eigenschaft des Problems und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
Wenn Sie nur eines mitnehmen
@@ -16172,7 +16172,7 @@ Mutterrolle 5,600 mm, 13 bestellte Breiten, 219 Zuschnitte.
Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil III)
-
🎯 Merksatz zum Diagramm Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die Nichtlinearität das Problem, geht es um Konvexität — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die Unsicherheit das Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
+
🎯 Merksatz zum Diagramm Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die Nichtlinearität das Problem, geht es um Konvexität — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die Unsicherheit das Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
Der häufigste Fehler an dieser Stelle ist, Unsicherheit durch Mittelwerte zu ersetzen und dann deterministisch zu rechnen. Warum das systematisch danebengeht, zeigt Kapitel 12 unter dem Namen Fluch des Durchschnitts.
Zwei weitere Kapitel schließen sich an, weil sie dieselbe Voraussetzung aufgeben, nur an einer anderen Stelle: Kapitel 14 gibt das eine Ziel auf, Kapitel 15 die Annahme, die Eingabedaten seien gegeben statt selbst geschätzt. Was dieser Teil insgesamt leistet, fasst die Synthese an seinem Ende zusammen.
Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität
📌 Kapitel auf einen Blick
-
Worum geht es? Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in Teil IV.
+
Worum geht es? Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in Teil IV.
Voraussetzungen:Kapitel 2 (Konvexität, Eigenwerte), Kapitel 5 (Dualität, Schattenpreise). Gradienten werden in Abschnitt 11.2 wiederholt.
-
Danach können Sie: Ein quadratisches Programm aufstellen, die KKT-Bedingungen anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären, eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
+
Danach können Sie: Ein quadratisches Programm aufstellen, die KKT-Bedingungen anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären, eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er negativ und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus Teil II — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
+
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er negativ und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus Teil II — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
🎯 Merksatzcp.quad_form(w, S) ist die Zeile, um die es im ganzen Kapitel geht. Sie ist genau dann harmlos, wenn \mathbf{S}positiv semidefinit ist — und genau dann ein Problem, wenn nicht. CVXPY prüft das für Sie und verweigert die Arbeit im Zweifelsfall. Diese Verweigerung ist ein Schutzmechanismus, keine Schikane.
@@ -16278,13 +16278,13 @@ Risiko der Mischung: 8.90 %
11.2 Lernziele
Nach diesem Kapitel können Sie …
-
… ein quadratisches Programm (QP) aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
+
… ein quadratisches Programm (QP) aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
… die KKT-Bedingungen herleiten und auf ein Optimierungsproblem anwenden.
… den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären.
… zwischen konvex und streng konvex unterscheiden und eine gültige, positiv semidefinite Kovarianzmatrix konstruieren.
Auffrischung: Gradient in einer Minute
-
Der Gradient\nabla f(\mathbf{x}) ist der Vektor aller partiellen Ableitungen:
+
Der Gradient\nabla f(\mathbf{x}) ist der Vektor aller partiellen Ableitungen:
@@ -16364,7 +16364,7 @@ Risiko der Mischung: 8.90 %
-
Warum das praktisch zählt: Eine Stichproben-Kovarianzmatrix mit mehr Titeln als Beobachtungen (N > T) ist singulär — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus Kapitel 18.
+
Warum das praktisch zählt: Eine Stichproben-Kovarianzmatrix mit mehr Titeln als Beobachtungen (N > T) ist singulär — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus Kapitel 18.
Alle drei Zeilen der Tabelle lassen sich an einem winzigen Zwei-Variablen-QP direkt beobachten — inklusive der Stelle, an der CVXPY ein nicht konvexes Problem verweigert, bevor überhaupt ein Solver aufgerufen wird:
2. Primale Zulässigkeit — die ursprünglichen Bedingungen gelten: g_i(\mathbf{x}^*)\le0, h_j(\mathbf{x}^*)=0.
3. Duale Zulässigkeit — die Multiplikatoren der Ungleichungen sind nichtnegativ: \lambda_i^*\ge0.
Warum? Eine Wand kann nur drücken, nicht ziehen. Ein negatives \lambda hieße, die Bedingung würde die Lösung von sich wegziehen — dann wäre sie nicht bindend.
-
4. Komplementärer Schlupf — \lambda_i^*\cdot g_i(\mathbf{x}^*)=0 für alle i.
Bedeutung: Entweder ist die Bedingung nicht bindend (g_i<0), dann muss\lambda_i=0 sein. Oder der Multiplikator ist positiv, dann muss die Bedingung mit Gleichheit binden. Das ist derselbe Satz wie in Kapitel 5 — nur allgemeiner formuliert.
11.5 Nichtlineare Optimierung mit scipy.optimize.minimize
-
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequential Least Squares Programming).
+
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequential Least Squares Programming).
⚠️ Der entscheidende Unterschied zu CVXPY CVXPY prüft die Konvexität und garantiert bei Erfolg das globale Optimum. scipy.optimize.minimize prüft nichts und liefert ein lokales Optimum, das vom Startpunkt abhängt (siehe die Demonstration in Abschnitt 2.5). Wer SLSQP einsetzt, sollte immer mit mehreren Startpunkten rechnen und die Ergebnisse vergleichen.
Praxisfall: Entropie-maximierte Kapitalallokation
-
Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:
+
Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:
Beweisbar globales Optimum, mit entsprechendem Rechenaufwand
@@ -17278,7 +17278,7 @@ Drei Konsequenzen fuer die Praxis:
Aufgabe 11.2 ⭐ — Komplementärer Schlupf. Ein QP liefert w = (0{,}4;\ 0{,}0;\ 0{,}6) mit Bedingung w \ge 0 und Multiplikatoren \lambda = (0{,}0;\ 0{,}03;\ 0{,}0). Ist das mit KKT verträglich? Was sagt \lambda_2 = 0{,}03 wirtschaftlich?
Aufgabe 11.3 ⭐⭐ — KKT von Hand. Lösen Sie mit KKT vollständig von Hand: \min\ x_1^2 + x_2^2 \qquad\text{u. d. N.}\qquad x_1 + x_2 \ge 4 Geben Sie x^*, \lambda^* und die Interpretation von \lambda^* an. Prüfen Sie mit CVXPY.
Aufgabe 11.4 ⭐⭐ — Ungültige Kovarianzmatrix erkennen. Prüfen Sie, ob folgende Korrelationsmatrix möglich ist, und begründen Sie: \mathbf{C} = \begin{pmatrix}1{,}0&0{,}9&-0{,}9\\0{,}9&1{,}0&0{,}9\\-0{,}9&0{,}9&1{,}0\end{pmatrix} (Tipp: Wenn A stark mit B korreliert und B stark mit C, kann A dann stark negativ mit C korrelieren?)
-
Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen. Variieren Sie in Entropie_Maximierte_Allokation.py systematisch \alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\} und \beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}. Stellen Sie für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar. (a) Wie verändert \beta die Konzentration im Krypto-Titel? (b) Ab welchem \beta nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
+
Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen. Variieren Sie in Entropie_Maximierte_Allokation.py systematisch \alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\} und \beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}. Stellen Sie für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar. (a) Wie verändert \beta die Konzentration im Krypto-Titel? (b) Ab welchem \beta nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
Aufgabe 11.6 ⭐⭐⭐ — Nicht-Konvexität demonstrieren. Bauen Sie bewusst eine ungültige „Kovarianzmatrix“ (per A@A.T mit anschließend überschriebener Diagonale) und lösen Sie das Modell mit 20 Startpunkten. Dokumentieren Sie: Wie viele verschiedene Optima entstehen? Wie groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die „Portfoliovarianz“ negativ werden?
11.8 Finde den Denkfehler
@@ -17292,7 +17292,7 @@ Drei Konsequenzen fuer die Praxis:
Alle drei schwanken mit 20 %. Leerverkäufe sind in seinem Mandat erlaubt, die Gewichte dürfen also negativ werden; sie müssen sich nur zu 100 % summieren.
Sein erster Versuch mit CVXPY bricht ab:
-
DCPError: Problem does not follow DCP rules.
+
[DCPError](#gloss:dcperror){.glossar-link}: Problem does not follow [DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link} rules.
The objective is not DCP. Its following subexpressions are not: QuadForm(...)
Er hält das für eine Einschränkung der Bibliothek und weicht auf scipy aus:
import numpy as np
@@ -17350,12 +17350,12 @@ The objective is not DCP. Its following subexpressions are not: QuadForm(...)Nicht-Konvexität kommt aus dem Alltag, nicht aus der Theorie: Mengenrabatte, Skaleneffekte, Produkte zweier Entscheidungen. Wo eines davon im Modell steht, ist die Optimalitätsgarantie weg.
Berichten Sie bei nicht-konvexen Problemen die Streuung mit, nicht die letzte Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein einzelner Wert ist es nicht.
-
Ausblick.Kapitel 12 gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
+
Ausblick.Kapitel 12 gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit
📌 Kapitel auf einen Blick
-
Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
+
Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
Voraussetzungen:Kapitel 5, Kapitel 11. Grundbegriffe der Statistik (Erwartungswert, Quantil).
Danach können Sie: Ein zweistufiges stochastisches Modell aufstellen, Monte-Carlo zur Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht.
🎯 Merksatz Die optimale Entscheidung unter Unsicherheit ist fast nie die Entscheidung, die für den Mittelwert optimal wäre. Sie hängt davon ab, welcher der beiden Fehler teurer ist — und verschiebt sich zu der Seite, auf der Irren billiger ist. Wer mit dem Mittelwert plant, hat diese Frage nie gestellt.
-
Dieses Muster heißt Newsvendor-Problem (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
+
Dieses Muster heißt Newsvendor-Problem (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
… den Fluch des Durchschnitts (Flaw of Averages) an einem Beispiel erklären.
… ein zweistufiges stochastisches Programm mit Recourse formulieren.
… Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
-
… ein robustes Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
-
… eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
+
… ein robustes Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
+
… eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
… begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
… das kritische Verhältnis eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen.
… eine Terminzusage als Quantil formulieren statt als Mittelwert.
@@ -17590,7 +17590,7 @@ Kosten fuer ausgewaehlte x zum Vergleich:
Das Optimum liegt exakt auf einem Szenariowert (250 = 'Volatil'),
nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.
Kein Rechentrick, sondern brute-force über alle ganzzahligen x von 0 bis 500 — das Optimum landet exakt dort, wo es die Handrechnung vorhersagt.
-
Warum? Die Kostenfunktion ist asymmetrisch: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die Jensensche Ungleichung: Für konvexe Kostenfunktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]) — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
+
Warum? Die Kostenfunktion ist asymmetrisch: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die Jensensche Ungleichung: Für konvexe Kostenfunktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]) — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
🎯 Merksatz Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt.
@@ -17611,8 +17611,8 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.<
@@ -17966,7 +17966,7 @@ den Mittelwert (225), weil Unterdeckung (120 EUR)
deutlich teurer ist als Leerlauf (5 EUR).
==================================================================================
Lesen Sie die Vergleichstabelle von unten nach oben. Die beiden extremen Haltungen sind die teuersten: Wer den Worst Case abdeckt (500 Einheiten), zahlt 5 000 € zu viel für Kapazität, die in 80 % der Fälle brachliegt. Wer optimistisch plant (100), zahlt 2 625 € Strafe für ständige Notzukäufe. Die naive Mittelwertplanung liegt dazwischen — aber eben auch nicht optimal.
-
Der EVPI von 7 375 € ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe EVPI interpretieren (Abschnitt 12.8).
+
Der EVPI von 7 375 € ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe EVPI interpretieren (Abschnitt 12.8).
💡 Was ist der EVPI? Der Expected Value of Perfect Information beziffert, wie viel eine perfekte Prognose wert wäre: die Differenz zwischen den Kosten unter Unsicherheit und den Kosten bei vollständigem Wissen. Er ist eine Obergrenze für jedes Prognoseprojekt. Wenn der EVPI bei 12 000 € pro Jahr liegt, lohnt sich keine Prognosesoftware für 50 000 € — selbst wenn sie perfekt wäre. Diese Zahl bewahrt Projekte vor teuren Fehlinvestitionen.
@@ -18087,7 +18087,7 @@ deutlich teurer ist als Leerlauf (5 EUR).
print(" als sie im Normalfall kostet.")print("="*88)
-
⚠️ Der Preis der Robustheit Robuste Modelle sind konservativ. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. Unsicherheitsmenge realistisch wählen. Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. Budgeted Uncertainty (Bertsimas/Sim): Man nimmt an, dass höchstens \Gamma von n Parametern gleichzeitig ihren Worst Case annehmen. \Gamma steuert die Vorsicht stufenlos. 3. Nutzen und Preis immer beziffern — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.
+
⚠️ Der Preis der Robustheit Robuste Modelle sind konservativ. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. Unsicherheitsmenge realistisch wählen. Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. Budgeted Uncertainty (Bertsimas/Sim): Man nimmt an, dass höchstens \Gamma von n Parametern gleichzeitig ihren Worst Case annehmen. \Gamma steuert die Vorsicht stufenlos. 3. Nutzen und Preis immer beziffern — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.
@@ -18100,7 +18100,7 @@ deutlich teurer ist als Leerlauf (5 EUR).
📐 Formel-Lesehilfe * \mathbf{a} — der unsichere Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist keine Zahl, sondern eine Zufallsgröße. * \mathbf{x} — Ihre Entscheidung. Sie fällt, bevor\mathbf{a} sich zeigt. * \alpha — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. \alpha = 0{,}05 heißt „in höchstens 5 % der Fälle darf es schiefgehen“.
Ohne Formel gesagt: „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“
-
Das Problem daran. So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
+
Das Problem daran. So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
Weg 1: Normalverteilung — die Bedingung wird ein Kegel
Ist \mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma}), dann ist \mathbf{a}^\top\mathbf{x} für festes \mathbf{x} selbst normalverteilt, mit Erwartungswert \hat{\mathbf{a}}^\top\mathbf{x} und Standardabweichung \sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}. Damit lässt sich die Wahrscheinlichkeit durch ein Quantil ersetzen:
@@ -18110,9 +18110,9 @@ deutlich teurer ist als Leerlauf (5 EUR).
\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad
z_{1-\alpha} = \Phi^{-1}(1-\alpha)
-
Das ist eine Second-Order-Cone-Bedingung — dieselbe Bauform, die Kapitel 11 eingeführt hat, und CVXPY löst sie ohne Umstände.
+
Das ist eine Second-Order-Cone-Bedingung — dieselbe Bauform, die Kapitel 11 eingeführt hat, und CVXPY löst sie ohne Umstände.
-
🎯 Warum eine Norm und keine Summe Der Sicherheitszuschlag \lVert\mathbf{L}^\top\mathbf{x}\rVert_2 ist keine Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken gemeinsam weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen.
+
🎯 Warum eine Norm und keine Summe Der Sicherheitszuschlag \lVert\mathbf{L}^\top\mathbf{x}\rVert_2 ist keine Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken gemeinsam weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen.
Weg 2: Szenarien — Big-M ohne Verteilungsannahme
Liegen statt einer Verteilung nur S beobachtete Szenarien vor, bekommt jedes eine Binärvariable z_s, die sagt, ob es verletzt werden darf:
Aus dem Kegelproblem wird ein MILP mit S Binärvariablen. Für M gilt, was Kapitel 6 über die Big-M-Falle sagt: so klein wie möglich. Hier ist M = b die kleinste gültige Schranke, denn \mathbf{a}_s^\top\mathbf{x} \ge 0 — größer kann eine Verletzung gar nicht ausfallen.
+
Aus dem Kegelproblem wird ein MILP mit S Binärvariablen. Für M gilt, was Kapitel 6 über die Big-M-Falle sagt: so klein wie möglich. Hier ist M = b die kleinste gültige Schranke, denn \mathbf{a}_s^\top\mathbf{x} \ge 0 — größer kann eine Verletzung gar nicht ausfallen.
Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus. Das ist kein Fehler, sondern die Definition des Erwartungswerts — und dieselbe Falle wie in Abschnitt 12.3, nur teurer. Er kauft die billigsten Quellen je erwarteter Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, Wind und Sonne schrumpfen auf einen Rest.
Zweitens: Sicherheit ist konvex bepreist. Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das 4,5-fache. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der nächste Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.
-
Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung. Der Plan verspricht 95 % und hält 87,4 %. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht alles zugleich ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
+
Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung. Der Plan verspricht 95 % und hält 87,4 %. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht alles zugleich ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf 94,8 %, kostet dafür 16,7 % mehr — und kauft keine Windkraft mehr. Die Logik dahinter ist hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich Bei S Szenarien und \alpha = 5\,\% darf das Modell genau 0{,}05 \cdot S davon ignorieren — und es sucht sich die teuersten aus. In der Stichprobe trifft es seine Quote dadurch immer; außerhalb streut sie.
Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei S = 200, 400, 800, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen 93,1 % und 96,7 %. Die Spanne wurde von S = 200 auf 400 enger (3,5 → 1,2 Prozentpunkte), bei S = 800 aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht.
-
Konsequenz für die Praxis: Eine mit Szenarien erkaufte Zusage immer an einer zurückgehaltenen Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in Kapitel 21.
+
Konsequenz für die Praxis: Eine mit Szenarien erkaufte Zusage immer an einer zurückgehaltenen Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in Kapitel 21.
🎯 Merksatz Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern.
Aufgabe 12.2 ⭐ — Ansatz wählen. Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? (e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält?
Aufgabe 12.3 ⭐⭐ — Zweistufiges Modell rechnen. Wiederholen Sie die Handrechnung Warum der Mittelwert in die Irre führt mit geänderten Kosten: Spot 60 € statt 120 €. (a) Wie ändert sich die optimale Kapazität? (b) Erklären Sie die Richtung der Änderung. (c) Bei welchem Spot-Preis wäre die Mittelwert-Planung optimal?
Aufgabe 12.4 ⭐⭐ — EVPI interpretieren. Ihr Modell liefert EVPI = 8 400 € pro Jahr. Ein Anbieter verlangt 15 000 € jährlich für eine Prognoselösung, die „80 % Treffsicherheit“ verspricht. Wie argumentieren Sie?
-
Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern. Erweitern Sie Monte_Carlo.py: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
+
Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern. Erweitern Sie Monte_Carlo.py: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen.Chance_Constraints.py beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das szenariobasierte Modell meldet dann bei 95 % infeasible, das analytische dagegen optimal. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten.
Aufgabe 12.7 ⭐⭐⭐ — Budgeted Uncertainty. Erweitern Sie Robuste_Optimierung.py um den Ansatz von Bertsimas/Sim: Höchstens \Gamma Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie \Gamma \in \{0, 1, 2, 3, 4\} und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei \Gamma ganzzahlig genügt es, die \Gamma größten \delta_i w_i abzuziehen — das lässt sich in CVXPY mit cp.sum_largest formulieren.)
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
1. Bei einem Ersatzteil kostet ein Stück zu wenig 1 400 €, ein Stück zu viel 120 €. Der Bedarf beträgt im Mittel 30 Stück. Wie viel bestellen Sie? (a) 30 — das ist der Erwartungswert des Bedarfs. (b) Deutlich mehr als 30, nämlich das 92,1-%-Quantil des Bedarfs. Das kritische Verhältnis 1400/(1400+120) sagt, wie weit man sich auf die günstigere Fehlerseite stellen soll. (c) Weniger als 30, weil Lagerhaltung Kapital bindet.
2. Was unterscheidet stochastische** von robuster Optimierung?** (a) Stochastische Optimierung ist genauer, robuste ist eine Näherung für schnelle Rechnungen. (b) Stochastische Optimierung braucht Wahrscheinlichkeiten und optimiert den Erwartungswert; robuste Optimierung braucht nur Bandbreiten und sichert den schlechtesten Fall darin ab. (c) Robuste Optimierung berücksichtigt mehr Szenarien.
-
3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch? (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen linear sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
+
3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch? (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen linear sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
Eine Zusage gilt nur für die unterstellte Verteilung. Ein Plan, der unter Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle nicht kannte, in der alles zugleich ausfällt. Szenariobasierte Zusagen wiederum überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden.
Beziffern Sie immer Preis und Nutzen der Absicherung. Ohne beide Zahlen ist die Entscheidung nicht begründbar.
-
Ausblick.Kapitel 13 fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
+
Ausblick.Kapitel 13 fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution
📌 Kapitel auf einen Blick
-
Worum geht es? Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.
+
Worum geht es? Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.
Voraussetzungen:Kapitel 12 (Erwartungswerte). Rekursion in Python.
-
Danach können Sie: Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
+
Danach können Sie: Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
🎯 Merksatz Eine Entscheidung ist nie für sich zu bewerten, sondern nur zusammen mit allem, was danach kommt. Genau das leistet die Wertfunktion: Sie fasst die gesamte Zukunft eines Zustands in einer einzigen Zahl zusammen. Und weil man die Zukunft kennen muss, bevor man die Gegenwart bewerten kann, rechnet dynamische Programmierung rückwärts.
-
Warum funktioniert das? Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt Bellmansches Optimalitätsprinzip und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
+
Warum funktioniert das? Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt Bellmansches Optimalitätsprinzip und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
📐 Formel-Lesehilfe * n_t — in Periode t verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * \eta („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * X_t — Restbestand nach dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * \lambda — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * \sigma — Volatilität je Periode.
+
📐 Formel-Lesehilfe * n_t — in Periode t verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * \eta („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * X_t — Restbestand nach dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * \lambda — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * \sigma — Volatilität je Periode.
Ohne Formel gesagt: „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet Nervenkitzel. Finde die Mitte.“
Die zwei Extremfälle: * \lambda \to 0 (risikoneutral): gleichmäßiges Aufteilen auf alle Perioden minimiert \sum n_t^2 bei fester Summe. * \lambda \to \infty (extrem risikoscheu): sofort alles verkaufen, um kein Risiko zu tragen.
Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie risk_aversion = 1e-6 würde dann in Wahrheit 0{,}1 bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
+
Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie risk_aversion = 1e-6 würde dann in Wahrheit 0{,}1 bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
Der Parameter lässt sich stattdessen herleiten. Beide Kostenterme werden in Euro gerechnet: * Marktauswirkung: \eta \cdot n_t^2 mit \eta in €/Stück². * Risiko: \tfrac{\lambda}{2}\cdot \sigma_{\text{Periode}}^2 \cdot P_0^2 \cdot X_t^2 — die Varianz des Werts der offenen Position, multipliziert mit der Risikoaversion.
Durch die Multiplikation mit P_0^2 (dem Quadrat des Aktienkurses) stimmen die Einheiten, und \lambda wird interpretierbar: Es ist der Preis, den man je Einheit Wertvarianz zu zahlen bereit ist.
@@ -19222,7 +19222,7 @@ Abweichung DP zur analytischen Loesung: 5.788 % (Diskretisierung: 1000 Stueck)
1e-03 | 97,000 ( 97.0 %) | fast alles sofort
====================================================================================
Die Lösung ist front-loaded: 41 % im ersten Schritt, dann fallend (25 %, 16 %, 10 %, 8 %). Das ist die typische Form — man baut Risiko früh ab, aber nicht abrupt. Die Sensitivitätstabelle zeigt, wie \lambda zwischen den beiden Extremen steuert: Bei \lambda = 10^{-7} verkauft das Modell gleichmäßig (Risiko ist egal), bei \lambda = 10^{-3} praktisch alles sofort (Risiko dominiert).
-
Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung 2 976 € oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
+
Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung 2 976 € oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
💻 Code-Durchgang: die analytische Gegenprobe und was ihre Abweichung bedeutet
Das Almgren-Chriss-Problem hat eine geschlossene Lösung: X_t = X_0\,\frac{\sinh(\kappa(T-t))}{\sinh(\kappa T)}, \qquad \kappa = \operatorname{arcosh}\!\Big(\tfrac{\tilde\kappa^2}{2}+1\Big),\qquad \tilde\kappa^2 = \frac{\lambda\,\sigma^2 P_0^2}{\eta}
Das ist der Fluch der Dimensionalität (Bellmans eigener Begriff): Jede zusätzliche Zustandsvariable multipliziert den Aufwand.
-
Gegenmittel: * Zustandsraumverkleinern: gröber diskretisieren, irrelevante Variablen weglassen. * Approximate Dynamic Programming:V_t durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * Reinforcement Learning: dieselbe Bellman-Gleichung, aber V wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * Nach geschlossenen Lösungen suchen — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.
+
Gegenmittel: * Zustandsraumverkleinern: gröber diskretisieren, irrelevante Variablen weglassen. * Approximate Dynamic Programming:V_t durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * Reinforcement Learning: dieselbe Bellman-Gleichung, aber V wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * Nach geschlossenen Lösungen suchen — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.
Aufgabe 13.1 ⭐ — Bausteine benennen. Ein Wanderer plant eine 5-Tages-Tour und muss täglich entscheiden, wie weit er läuft. Bestimmen Sie Stufe, Zustand, Aktion und Wertfunktion. Was gehört nicht in den Zustand?
Aufgabe 13.2 ⭐ — Optimalitätsprinzip anwenden. Warum folgt aus dem Optimalitätsprinzip, dass man rückwärts rechnen darf? Was würde schiefgehen, wenn die Kosten einer Periode auch von früheren Aktionen abhängen (und nicht nur vom aktuellen Zustand)?
Aufgabe 13.3 ⭐⭐ — Rückwärtsinduktion von Hand. 5 Einheiten in 3 Perioden, Kosten C(n) = n^2 + 2n. Erstellen Sie die vollständige Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit Bellman_Minimalbeispiel.py (angepasst).
-
Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP. Lösen Sie das Rucksackproblem aus Kapitel 6 mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.
+
Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP. Lösen Sie das Rucksackproblem aus Kapitel 6 mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.
Aufgabe 13.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Untersuchen Sie mit Mehrperiodige_Order_Execution.py: (a) Bei welchem \lambda verkauft das Modell in der ersten Periode mehr als 50 %? (b) Stellen Sie den Zusammenhang zwischen \lambda und den erwarteten Gesamtkosten dar. (c) Ein Händler sagt: „Ich will höchstens 20 % Marktauswirkungskosten und den Rest an Risiko.“ Welches \lambda setzen Sie?
Aufgabe 13.6 ⭐⭐⭐ — Zustandsraum erweitern. Erweitern Sie das Ausführungsmodell um einen zweiten Zustand: die aktuelle Orderbuchtiefe (3 Stufen: dünn/normal/tief), die mit gegebenen Übergangswahrscheinlichkeiten wechselt und \eta um Faktor 2 / 1 / 0,5 skaliert. Wie ändert sich die Strategie? Wie stark wächst die Rechenzeit?
Worum geht es? Um den Normalfall, den die bisherigen Kapitel umgangen haben: Es gibt nicht ein Ziel, sondern zwei, die gegeneinander stehen. Kosten und CO₂, Termintreue und Bestand, Rendite und Risiko.
Danach können Sie: begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse grundsätzlich nicht erreichen kann; eine vollständige Pareto-Front mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
+
Danach können Sie: begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse grundsätzlich nicht erreichen kann; eine vollständige Pareto-Front mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
Eine Spedition vergibt fünf Sendungen an LKW, Bahn oder Kombinierten Verkehr. Der LKW ist billig und schmutzig, die Bahn teuer und sauber — und es sind nur zwei Trassen frei.
Statt zu fragen „wie wichtig ist uns CO₂?” fragen wir: Was bekommen wir für zwei Prozent mehr Geld?
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}# LKW Bahn Kombi LKW billig+schmutzig, Bahn teuer+sauberkosten = np.array([[ 900, 1150, 1020], [ 620, 790, 700], [1300, 1660, 1470],
@@ -19910,10 +19910,10 @@ dem CO2-Preis, den das Unternehmen ohnehin ansetzt.
Eine interaktive Fassung dieser Grafik steht auf den Kapitelseiten der Website bereit.
Warum es keine Frage des Rasters ist. „c(x) + w\,e(x) minimieren” heißt geometrisch: eine Gerade der Steigung -1/w von links unten an die Punktwolke schieben und schauen, welchen Punkt sie zuerst berührt. Eine Gerade berührt immer einen Eckpunkt der unteren konvexen Hülle. Punkte, die oberhalb der Hülle liegen, werden von keiner Geraden zuerst getroffen — für kein w, bei beliebig feiner Abstufung.
Das Programm rechnet diese Hülle in untere_huelle() unabhängig aus und vergleicht: Genau die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe steht in der Ausgabe.
-
Was Sie in der Abbildung sehen. Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht w. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in Abschnitt 14.7 geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
+
Was Sie in der Abbildung sehen. Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht w. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in Abschnitt 14.7 geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
⚠️ Der Grund liegt in der Ganzzahligkeit
-
Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen ganzzahlig werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
+
Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen ganzzahlig werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
Deshalb betrifft dieses Problem praktisch jedes betriebliche Mehrzielmodell. Wer mit Gewichten arbeitet, verliert nicht ein paar Nachkommastellen, sondern ganze Alternativen — im Beispiel 40 % der Front.
Das Gewicht ist keine Feineinstellung
@@ -19990,7 +19990,7 @@ e(x) \le \varepsilon
\text{Schattenpreis`\index{Schattenpreis}`{=latex}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
-
Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.
+
Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.
💻 Code-Durchgang: die drei Bausteine
@@ -20029,7 +20029,7 @@ e(x) \le \varepsilon
Aufgabe 14.1 ⭐ — Dominanz prüfen. Plan P kostet 14 000 € und stößt 6 000 kg aus, Plan Q kostet 13 800 € und stößt 6 100 kg aus. Dominiert einer den anderen? Und wie steht es mit R (14 000 €, 6 100 kg)?
Aufgabe 14.2 ⭐ — Der Schattenpreis in der Praxis. Der interne CO₂-Preis des Unternehmens beträgt 0,90 € je Kilogramm. Welchen Punkt der Front würden Sie empfehlen — und wie begründen Sie ihn in einem Satz?
Aufgabe 14.3 ⭐⭐ — Mehr Trassen. Erhöhen Sie BAHN_TRASSEN von 5 auf 8. Was passiert mit der Länge der Front und mit der Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.
-
Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation. Lassen Sie integrality weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
+
Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation. Lassen Sie integrality weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
Aufgabe 14.5 ⭐⭐⭐ — Drei Ziele. Ergänzen Sie die Laufzeit als drittes Ziel (LKW schnell, Bahn langsam). Die Front wird zu einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand so schnell?
14.10 Finde den Denkfehler
@@ -20055,7 +20055,7 @@ e(x) \le \varepsilon
14.12 Selbsttest
Erklären Sie Pareto-Dominanz an einem Beispiel aus Ihrem Arbeitsumfeld.
-
Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem MILP nicht?
+
Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem MILP nicht?
Ein Kollege sagt: „Ich nehme w = 0{,}5, das ist neutral.” Was ist daran falsch?
Beschreiben Sie das ε-Constraint-Verfahren in drei Sätzen, ohne Formeln.
Sie legen einer Geschäftsführung eine Front mit zwölf Punkten vor. Welche Spalte brauchen Sie, damit die Entscheidung fallen kann?
@@ -20075,8 +20075,8 @@ e(x) \le \varepsilon
📌 Kapitel auf einen Blick
Worum geht es? Um die Naht zwischen zwei Welten, die in getrennten Abteilungen sitzen: Ein Modell prognostiziert, ein anderes entscheidet. Beide arbeiten sauber — und genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.
-
Voraussetzungen:Kapitel 12, insbesondere das Newsvendor-Problem und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
-
Danach können Sie: begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige Zielgröße einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
+
Voraussetzungen:Kapitel 12, insbesondere das Newsvendor-Problem und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
+
Danach können Sie: begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige Zielgröße einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein Fehlermaß — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
+
Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein Fehlermaß — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
Der Fehler steckt nicht in einer der beiden Hälften, sondern in der Naht:
Das übliche zweistufige Vorgehen: erst eine unbekannte Größe schätzen, dann mit der Schätzung optimieren, als wäre sie die Wahrheit.
Das Problem: Die erste Stufe wird auf ein statistisches Maß trainiert, die zweite erzeugt ökonomische Kosten. Niemand garantiert, dass ein besseres statistisches Maß zu geringeren Kosten führt — und in diesem Kapitel ist es nachweislich umgekehrt.
@@ -20620,7 +20620,7 @@ Kosten statt auf Fehler trainiert.
15.7 Wie weit das Verfahren reicht
Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert” — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: das Prognosemodell direkt auf die Entscheidungskosten zu trainieren statt auf ein Fehlermaß.
-
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man durch das Optimierungsproblem hindurch ableiten.
+
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man durch das Optimierungsproblem hindurch ableiten.
@@ -20672,7 +20672,7 @@ Kosten statt auf Fehler trainiert.
🐛 „Wir haben die Prognose um 18 % verbessert”
Ein Data-Science-Team stellt sein Quartalsergebnis vor:
-
„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte.”
+
„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte.”
Sechs Wochen später meldet die Disposition, die Retouren seien gestiegen.
Das Team prüft alles nach: Die Modellgüte stimmt, die Testdaten waren sauber getrennt, es gibt kein Leck. Das neue Modell prognostiziert die Nachfrage tatsächlich deutlich besser als das alte.
@@ -20729,7 +20729,7 @@ Kosten statt auf Fehler trainiert.
@@ -20799,12 +20799,12 @@ Kosten statt auf Fehler trainiert.
Drei Fehler, die dieser Teil verhindert
Unsicherheit durch Mittelwerte ersetzen. Der Durchschnittskunde kauft nie. Bei asymmetrischen Kosten liegt das Optimum systematisch neben dem Mittelwert, und eine Terminzusage ist ein Quantil, kein Erwartungswert.
-
Konvexität voraussetzen, ohne sie zu prüfen. Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht von selbst.
+
Konvexität voraussetzen, ohne sie zu prüfen. Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht von selbst.
Zwei Ziele mit einem Gewicht verrechnen, bevor die Front bekannt ist. Ein Gewicht ist eine Antwort auf eine Frage, die noch niemand gestellt hat. Die Pareto-Front stellt sie zuerst.
Wenn Sie nur eines mitnehmen
-
🎯 Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet Zustandsraum, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, welche Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
+
🎯 Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet Zustandsraum, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, welche Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte
@@ -20818,7 +20818,7 @@ Kosten statt auf Fehler trainiert.
📌 Für wen dieser Teil gedacht ist
Die Finanzkapitel ab Kapitel 18 sind eine Vertiefungsdomäne, kein Pflichtstoff. Wer beruflich mit Logistik, Produktion oder Energie zu tun hat, liest die ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas fehlt — die Methoden sind dieselben.
-
Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen: Schätzfehler in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
+
Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:Schätzfehler in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
Ein Hinweis vorweg: Die Kapitel ab Kapitel 18 laden aktuelle Kursdaten über yfinance und brauchen dafür einen Internetzugang. Weil die Daten sich täglich ändern, sind die abgedruckten Zahlen dort Beispielläufe, keine reproduzierbaren Werte — anders als im übrigen Buch. Das Brückenkapitel kommt ohne Internet aus.
@@ -20826,7 +20826,7 @@ Kosten statt auf Fehler trainiert.
📌 Kapitel auf einen Blick
Worum geht es? Um den Nachweis, dass die Verfahren aus Teil II und III unverändert in der Finanzwelt funktionieren — und um die drei Stellen, an denen die Analogie endet.
Danach können Sie: ein betriebswirtschaftliches Modell in die andere Domäne übersetzen, ohne den Modellcode anzufassen; Schattenpreise in beiden Welten lesen; und benennen, welche Annahmen dabei nicht mitwandern.
Und jetzt der Punkt. Die Klasse heißt Produktionsproblem, und das Depot passt hinein, ohne dass eine Zeile geändert wurde. Kein Adapter, keine Unterklasse, keine Fallunterscheidung — die Struktur ist dieselbe, weil das Problem dasselbe ist: knappe Größen auf konkurrierende Verwendungen verteilen.
-
Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. 60,26 € je Einheit Risikobudget — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der Preis des Risikos. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis aus Kapitel 5.
+
Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. 60,26 € je Einheit Risikobudget — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der Preis des Risikos. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis aus Kapitel 5.
🎯 Merksatz Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei Datensätze. Ob das der Fall ist, erkennt man daran, dass derselbe Code ohne Änderung läuft — nicht daran, dass eine Tabelle es behauptet.
-
Warum funktioniert das? Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
+
Warum funktioniert das? Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
16.2 Lernziele
Nach diesem Kapitel können Sie …
… ein Allokationsproblem aus der einen Domäne in die andere übersetzen, indem Sie die Daten austauschen statt des Codes.
… den Schattenpreis in beiden Welten deuten — als Wert einer Ressource und als Preis des Risikos.
-
… erklären, warum die CVaR-Formulierung aus Kapitel 20 auf Lieferverzüge genauso passt wie auf Kursverluste.
+
… erklären, warum die CVaR-Formulierung aus Kapitel 20 auf Lieferverzüge genauso passt wie auf Kursverluste.
… die drei Stellen benennen, an denen die Analogie nicht trägt — und begründen, warum das Übertragen von Methoden dort gefährlich wird.
Aufgabe 16.1 ⭐ — Die dritte Ressource. Ergänzen Sie im Depot eine dritte knappe Größe: „Liquidität” (wie schnell sich eine Anlage verkaufen lässt). Was entspricht ihr in der Werkstatt?
Aufgabe 16.2 ⭐ — Den Schattenpreis lesen. Das Risikobudget hat einen Schattenpreis von 60,26 €. Die Aufsicht bietet an, es gegen eine Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — und welche Einschränkung dieser Aussage kennen Sie aus Abschnitt 5.9?
-
Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts. Übertragen Sie die Ledoit-Wolf-Shrinkage aus Kapitel 18 auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
+
Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts. Übertragen Sie die Ledoit-Wolf-Shrinkage aus Kapitel 18 auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
Aufgabe 16.4 ⭐⭐ — CVaR mit Ganzzahligkeit. Der Einkauf darf höchstens drei Lieferanten beauftragen. Ergänzen Sie die entsprechende Kardinalitätsbedingung (Kapitel 6, Muster 5) und vergleichen Sie den CVaR mit dem teilbaren Fall. Was kostet die Ganzzahligkeit?
Aufgabe 16.5 ⭐⭐⭐ — Die eigene Brücke. Nehmen Sie ein Modell aus Ihrem Arbeitsumfeld und suchen Sie die Entsprechung in der jeweils anderen Welt. Prüfen Sie mit der Tabelle aus Abschnitt 16.5, ob die Übertragung trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
Eine Logistikerin liest dieses Kapitel und überträgt es zurück. Ihre Aufgabe: Aus 40 möglichen Lieferanten sollen die besten ausgewählt und Bestellmengen zugeteilt werden. Sie geht vor wie im Portfoliomanagement:
-
„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio.”
+
„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio.”
Das Vorgehen ist methodisch sauber, die Rechnung stimmt, und das Ergebnis sieht plausibel aus.
Zwei Dinge daran sind falsch — und das eine ist genau der Fehler, vor dem dieses Kapitel warnt, das andere ein zusätzlicher, den es nicht behandelt. Welche?
Und jetzt der Punkt. Bis knapp vier Stunden ist die fünfmal teurere Gasturbine die günstigere Wahl. Erst danach hat der Kernblock seine Anfahrkosten wieder eingespielt.
Die Merit-Order — Kraftwerke nach Grenzkosten sortieren und von unten auffüllen — ist damit nur für einen einzelnen Zeitpunkt richtig. Sobald ein Tag geplant wird, hängt jede Stunde an allen anderen: Ein Block, der um 8 Uhr angefahren wird, muss bis mindestens 16 Uhr laufen, und diese Entscheidung fällt am Vorabend.
-
🎯 Merksatz Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen ein Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.
+
🎯 Merksatz Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen ein Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.
Warum funktioniert das? Der Umschlagpunkt ist eine einfache Rechnung: Die Anfahrkostendifferenz (38 500 €) geteilt durch die Grenzkostendifferenz je Stunde (83 €/MWh × 120 MW = 9 960 €/h) ergibt 3,9 Stunden. Bei zwei Kraftwerken kann man das im Kopf ausrechnen — bei fünf Blöcken und 24 Stunden nicht mehr.
Aufgabe 17.2 ⭐ — Die fehlende Nebenbedingung. Das Modell kennt Mindestlaufzeiten, aber keine Mindeststillstandszeiten. Formulieren Sie die entsprechende Bedingung. Welchen realen Sachverhalt bildet sie ab?
Aufgabe 17.3 ⭐⭐ — Wie viele Szenarien? Rechnen Sie mit 10, 20, 40 und 80 Szenarien. Ab wann ändert sich der Commitment-Plan nicht mehr? Was folgt daraus für die Praxis — und was hat das mit Abschnitt 15.6 zu tun?
Aufgabe 17.4 ⭐⭐ — Der Wert eines Speichers. Ergänzen Sie einen Batteriespeicher (100 MW, 400 MWh, Wirkungsgrad 90 %). Um wie viel sinken die erwarteten Kosten — und was ist der Speicher damit je MWh Kapazität wert?
-
Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird. Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und Rechenzeit (Abschnitt 6.8). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus Kapitel 9 würden Sie einsetzen?
+
Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird. Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und Rechenzeit (Abschnitt 6.8). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus Kapitel 9 würden Sie einsetzen?
Worum geht es? Um die Eingangsdaten der Portfoliooptimierung — und darum, warum sie trügerisch sind. Wer historische Kovarianzen ungefiltert in einen Optimierer gibt, optimiert Rauschen.
Voraussetzungen:Kapitel 11 (Eigenwerte, positive Definitheit), Grundlagen der Statistik.
-
Danach können Sie: Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage anwenden — und erkennen, wann eine Kovarianzmatrix aus zu wenigen Beobachtungen stammt.
+
Danach können Sie: Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage anwenden — und erkennen, wann eine Kovarianzmatrix aus zu wenigen Beobachtungen stammt.
Das Problem: \mathbf{S} hat \frac{N(N+1)}{2} zu schätzende Parameter, aber nur T \cdot N Datenpunkte. Bei N = 50 Titeln sind das 1275 Parameter — geschätzt aus einem Jahr Daten (T = 252).
+
Das Problem: \mathbf{S} hat \frac{N(N+1)}{2} zu schätzende Parameter, aber nur T \cdot N Datenpunkte. Bei N = 50 Titeln sind das 1275 Parameter — geschätzt aus einem Jahr Daten (T = 252).
Die Zufallsmatrizentheorie (Random Matrix Theory, Marchenko-Pastur-Gesetz) zeigt: Die kleinsten Eigenwerte von \mathbf{S} werden systematisch unterschätzt, die größten überschätzt. Und genau das ist fatal, denn:
+
Die Zufallsmatrizentheorie (Random Matrix Theory, Marchenko-Pastur-Gesetz) zeigt: Die kleinsten Eigenwerte von \mathbf{S} werden systematisch unterschätzt, die größten überschätzt. Und genau das ist fatal, denn:
🎯 Der Error-Maximizer-Effekt Ein Risikominimierer sucht die Richtungen mit der kleinsten Varianz — also genau jene Eigenrichtungen, deren Eigenwerte am stärksten nach unten verzerrt sind. Die Optimierung greift damit zielsicher in das Schätzrauschen hinein. Markowitz-Optimierung ist deshalb kein Fehlerdämpfer, sondern ein Fehlerverstärker.
Diese Tabelle ist das Kernargument des ganzen Kapitels. Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit 75,5 % mehr Risiko als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das geschätzte Risiko — und die Differenz ist der Schätzfehler, in den er hineinoptimiert hat.
+
Diese Tabelle ist das Kernargument des ganzen Kapitels. Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit 75,5 % mehr Risiko als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das geschätzte Risiko — und die Differenz ist der Schätzfehler, in den er hineinoptimiert hat.
Drei Beobachtungen:
Der Schaden wächst dramatisch, je knapper die Daten. Von T/N = 31{,}5 (+1,6 %) bis T/N = 1{,}5 (+75,5 %) ist es kein gleitender Übergang, sondern eine Explosion.
Aufgabe 18.2 ⭐ — Mittelwertfalle. Ein Fonds meldet Jahresrenditen von +60\,\%, -40\,\%, +60\,\%, -40\,\%. (a) Wie hoch ist das arithmetische Mittel? (b) Wie viel Kapital hat ein Anleger nach 4 Jahren aus 10 000 €? (c) Welche jährliche Rendite entspricht dem tatsächlich?
Aufgabe 18.3 ⭐⭐ — Konditionszahl verstehen. Erzeugen Sie eine Kovarianzmatrix für N = 30 Titel aus T = 40, T = 100 und T = 1000 simulierten Beobachtungen. Berechnen Sie jeweils kleinsten Eigenwert und Konditionszahl. Ab welchem T wird die Matrix brauchbar?
Aufgabe 18.4 ⭐⭐ — Spaltenreihenfolge prüfen. Laden Sie fünf Ticker in einer bewusst unsortierten Reihenfolge. Zeigen Sie, dass raw["Close"].columns abweicht, und dass raw["Close"][tickers] das behebt. Was passiert, wenn ein Ticker nicht existiert?
-
Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen. Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von \delta nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit sklearn anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines GMV-Portfolios (nach dem Muster von Schaetzrauschen_Demo.py), (c) der Stabilität der Gewichte über rollierende Fenster.
+
Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen. Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von \delta nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit sklearn anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines GMV-Portfolios (nach dem Muster von Schaetzrauschen_Demo.py), (c) der Stabilität der Gewichte über rollierende Fenster.
Aufgabe 18.6 ⭐⭐⭐ — Error-Maximizer selbst messen. Erweitern Sie Schaetzrauschen_Demo.py um eine dritte Variante: die Gleichgewichtung (w_i = 1/N), die überhaupt keine Schätzung benötigt. (a) Ab welchem T/N schlägt die Stichproben-Optimierung die Gleichgewichtung? (b) Ab welchem T/N schlägt Ledoit-Wolf sie? (c) Was folgt daraus für die Praxis?
18.8 Finde den Denkfehler
@@ -23134,8 +23134,8 @@ Ausfallraten und jede andere geschaetzte Groesse.
📌 Kapitel auf einen Blick
Worum geht es? Um das Modell, das 1952 die quantitative Finanzwirtschaft begründete — und um die Restriktionen, ohne die es in der Praxis unbrauchbar bleibt.
Danach können Sie: Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die Korn-Transformation bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
Danach können Sie: Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die Korn-Transformation bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
Zeitbedarf: ca. 6 Stunden.
Programme: Diversifikation_Demo.py
@@ -23181,9 +23181,9 @@ Ausfallraten und jede andere geschaetzte Groesse.
mu = np.array([0.03, 0.08, 0.055]) # erwartete Renditesig = np.array([0.05, 0.18, 0.11]) # SchwankungR = np.array([[1.0, 0.1, 0.2], [0.1, 1.0, 0.5], [0.2, 0.5, 1.0]])
-S = np.outer(sig, sig) * R # Kovarianzmatrix
+S = np.outer(sig, sig) * R # [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}
-w, ziel = cp.Variable(3, nonneg=True), cp.Parameter(nonneg=True)
+w, ziel = cp.Variable(3, nonneg=True), cp.[Parameter](#gloss:parameter){.glossar-link}(nonneg=True)problem = cp.Problem(cp.Minimize(cp.quad_form(w, S)), [cp.sum(w) ==1, mu @ w >= ziel])
@@ -23206,13 +23206,13 @@ Ausfallraten und jede andere geschaetzte Groesse.
Bei 3 % Zielrendite: 0,69 — das Portfolio ist sicher, aber lässt Ertrag liegen.
Bei 4 %: 0,75 — das beste Verhältnis.
-
Bei 8 %: 0,44 — das renditestärkste Portfolio ist das schlechteste je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine Diversifikation mehr.
+
Bei 8 %: 0,44 — das renditestärkste Portfolio ist das schlechteste je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine Diversifikation mehr.
Von 3 % auf 8 % Rendite ist ein Zuwachs um Faktor 2,7 — das Risiko steigt dabei um Faktor 3,7. Die letzten Prozentpunkte Rendite sind die teuersten.
🎯 Merksatz Ein Optimierer beantwortet nicht die Frage „was soll ich tun?“, sondern „was ist erreichbar?“. Die Effizienzlinie ist eine Speisekarte, keine Empfehlung. Welche Zeile die richtige ist, hängt von der Risikotragfähigkeit ab — und das ist keine mathematische, sondern eine unternehmerische Frage.
-
Warum funktioniert das? Weil cp.Parameter das Modell nur einmal aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter in CVXPY: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand (Abschnitt 3.8). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
+
Warum funktioniert das? Weil cp.Parameter das Modell nur einmal aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter in CVXPY: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand (Abschnitt 3.8). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
📐 Formel-Lesehilfe Zähler: Überrendite über den risikolosen Zins r_f. Nenner: die Volatilität.
+
📐 Formel-Lesehilfe Zähler: Überrendite über den risikolosen Zins r_f. Nenner: die Volatilität.
Ohne Formel gesagt: „Wie viel Mehrertrag bekomme ich je Einheit Risiko, die ich eingehe?“ Eine Sharpe Ratio von 0,8 heißt: Für jeden Prozentpunkt Volatilität gibt es 0,8 Prozentpunkte Überrendite.
⚠️ Zur Reproduzierbarkeit Dieses Programm lädt Live-Daten. Ihre Zahlen werden abweichen. Prüfen Sie stattdessen die Struktur des Ergebnisses: Hält die Sektorgrenze? Summieren sich die Gewichte auf 100 %? Liegt die Sharpe Ratio des Max-Sharpe-Portfolios über der des GMV? Genau dafür sind die assert-Prüfungen da.
-
🎯 Der Realitätscheck: Gleichgewichtung Das Programm vergleicht immer mit dem 1/N-Portfolio. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen Schätzfehler enthält. Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert. Diese Messlatte sollte in jedem Portfolioprojekt stehen.
+
🎯 Der Realitätscheck: Gleichgewichtung Das Programm vergleicht immer mit dem 1/N-Portfolio. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen Schätzfehler enthält. Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert. Diese Messlatte sollte in jedem Portfolioprojekt stehen.
Aufgabe 19.3 ⭐⭐ — Korn-Transformation nachvollziehen. Zeigen Sie rechnerisch, dass die Sharpe Ratio invariant gegenüber positiver Skalierung ist: \text{SR}(c\,\mathbf{w}) = \text{SR}(\mathbf{w}) für c > 0. Warum gilt das nicht, wenn die Nebenbedingung \sum w_i = 1 mitgeführt wird — und wie löst die Transformation dieses Problem?
Aufgabe 19.4 ⭐⭐ — Restriktionen kosten Rendite. Lassen Sie Markowitz_CVXPY.py mit MAX_GEWICHT von 1,0 (keine Grenze) bis 0,10 in Schritten von 0,05 laufen. Tabellieren Sie Sharpe Ratio und maximal erreichbare Rendite. (a) Was kostet die 20-%-Grenze an Sharpe Ratio? (b) Ab welcher Grenze wird das Problem unlösbar? Warum?
Aufgabe 19.5 ⭐⭐ — Den Sektorfehler nachstellen. Bauen Sie den Fehler bewusst nach: Verwenden Sie tech_indices = [0,1,2,3] auf den alphabetisch sortierten Spalten. Vergleichen Sie das Ergebnis mit der korrekten Version. (a) Welche Titel werden tatsächlich beschränkt? (b) Wie stark unterscheiden sich die Portfoliogewichte? (c) Fällt der Fehler in den ausgegebenen Kennzahlen auf?
-
Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen. Erweitern Sie das Modell um „höchstens K = 5 Titel“ mit Binärvariablen. CVXPY braucht dafür cp.Variable(n, boolean=True) und einen MIQP-fähigen Solver (z. B. SCIP über cp.SCIP oder ECOS_BB). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
+
Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen. Erweitern Sie das Modell um „höchstens K = 5 Titel“ mit Binärvariablen. CVXPY braucht dafür cp.Variable(n, boolean=True) und einen MIQP-fähigen Solver (z. B. SCIP über cp.SCIP oder ECOS_BB). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
Aufgabe 19.7 ⭐⭐⭐ — Out-of-Sample-Test. Teilen Sie die Historie in zwei Hälften. Optimieren Sie auf der ersten, bewerten Sie auf der zweiten. Vergleichen Sie GMV, Max Sharpe und 1/N. (a) Welches Portfolio gewinnt out of sample? (b) Wie ändert sich das Bild mit Ledoit-Wolf gegenüber der Stichprobenkovarianz? (c) Welche Schlussfolgerung ziehen Sie für die Praxis?
19.8 Finde den Denkfehler
-
Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der Nebenbedingung, fällt es schwerer auf.
+
Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der Nebenbedingung, fällt es schwerer auf.
🐛 Finde den Denkfehler: Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio
Ein Analyst optimiert ein Portfolio aus zwölf Anlagen. Erwartete Renditen und Kovarianzen schätzt er aus einem Jahr Tagesdaten — 250 Beobachtungen, in der Praxis durchaus üblich.
@@ -24112,8 +24112,8 @@ Drei praktische Konsequenzen:
📌 Kapitel auf einen Blick
Worum geht es? Um zwei Schwächen des Markowitz-Modells: Es unterschätzt Extremverluste und ignoriert die Kosten des Umschichtens. Beide lassen sich mit konvexen Mitteln beheben.
Danach können Sie: VaR und CVaR unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die L_1-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
Danach können Sie:VaR und CVaR unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die L_1-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
… Einheiten konsistent halten und Nebenbedingungen vektorisieren.
… erklären, warum zwei Anlagen mit identischem VaR völlig verschiedene Extremverluste haben können.
-
… die Folgen fehlender Subadditivität für die Verteilung von Risikobudgets benennen.
+
… die Folgen fehlender Subadditivität für die Verteilung von Risikobudgets benennen.
20.3 Die zwei Schwächen des Markowitz-Modells
@@ -24324,7 +24324,7 @@ VaR: Portfolio 98.00 vs. Summe -4.00 -> VERLETZT die Subadditivitaet!
CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
========================================================================================
Die 6-Sigma-Zeile setzt die Sache ins Verhältnis: Unter Normalverteilung wäre ein solcher Tag ein Ereignis von einmal in zwei Millionen Jahren. Unter der t-Verteilung mit drei Freiheitsgraden — die realen Aktienrenditen deutlich näher kommt — passiert er etwa alle zwei Jahre. Der Faktor beträgt fast eine Million.
-
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen Gewinn von 2 €, im Portfolio einen Verlust von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung senkt das Risiko, wie es sein muss.
+
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen Gewinn von 2 €, im Portfolio einen Verlust von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung senkt das Risiko, wie es sein muss.
Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie VaR_CVaR_Demo.py. Erzeugt von bilder_04/erzeuge_var_vs_cvar.py.
@@ -24341,7 +24341,7 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py.
Value at Risk (\text{VaR}_\alpha): Der Verlust, der mit Wahrscheinlichkeit \alpha nicht überschritten wird.
-
Conditional Value at Risk (\text{CVaR}_\alpha, auch Expected Shortfall): Der durchschnittliche Verlust in den schlimmsten (1-\alpha) Prozent der Fälle.
+
Conditional Value at Risk (\text{CVaR}_\alpha, auch Expected Shortfall): Der durchschnittliche Verlust in den schlimmsten (1-\alpha) Prozent der Fälle.
📐 Der Unterschied in einem Satz Der VaR sagt: „In 95 % der Tage verlieren Sie höchstens 1,86 %.“ Der CVaR sagt: „Und wenn es doch schiefgeht, verlieren Sie im Mittel 2,99 %.“
Beide Zahlen stammen aus der Verteilung oben — nachzurechnen mit bilder_04/erzeuge_var_cvar.py.
📐 Formel-Lesehilfe — der Trick in drei Schritten * \gamma ist eine Hilfsvariable, die im Optimum automatisch den VaR annimmt. Man muss ihn also nicht vorher kennen — das ist der eigentliche Durchbruch. * -\mathbf{R}_s^\top\mathbf{w} ist der Verlust im Szenario s (Rendite mit negativem Vorzeichen). * u_s \ge \text{Verlust}_s - \gamma zusammen mit u_s \ge 0 bedeutet: u_s = \max(\text{Verlust}_s - \gamma,\ 0) — der Überschuss über die Schwelle. Liegt der Verlust unter \gamma, ist u_s = 0 und das Szenario zählt nicht.
Ohne Formel gesagt: „Wähle eine Schwelle \gamma. Zähle für jedes Szenario, wie weit der Verlust darüber hinausgeht. Der CVaR ist die Schwelle plus der gemittelte Überschuss — und zwar für diejenige Schwelle, bei der diese Summe minimal wird.“
-
Warum u_s automatisch das Maximum wird: Die Zielfunktion minimiert die Summe der u_s. Jedes u_s wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
+
Warum u_s automatisch das Maximum wird: Die Zielfunktion minimiert die Summe der u_s. Jedes u_s wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
🔤 Formel-Übersetzer: jedes Zeichen der Zielfunktion
In einem Satz:Der CVaR ist der Mittelwert der schlimmsten 1-\alpha Prozent — der Bruch vor der Summe sorgt allein dafür, dass durch die richtige Anzahl geteilt wird.
-
⚠️ Die teuerste Verwechslung des Kapitels > \alpha und 1-\alpha zu vertauschen führt zu keiner Fehlermeldung. Das Modell > rechnet weiter, nur eben über die falsche Menge. An 2 000 simulierten Tagesrenditen mit > Fat Tails gemessen: > > | Rechnung | gemittelt über | CVaR | > | — | — | — | > | richtig (\alpha = 0{,}95, Rand 5 %) | 100 von 2 000 Szenarien | 3,43 % | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | 0,18 % | > > Ein Faktor 19 — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > Die Gegenprobe kostet eine Zeile: Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt \alpha nicht.
+
⚠️ Die teuerste Verwechslung des Kapitels > \alpha und 1-\alpha zu vertauschen führt zu keiner Fehlermeldung. Das Modell > rechnet weiter, nur eben über die falsche Menge. An 2 000 simulierten Tagesrenditen mit > Fat Tails gemessen: > > | Rechnung | gemittelt über | CVaR | > | — | — | — | > | richtig (\alpha = 0{,}95, Rand 5 %) | 100 von 2 000 Szenarien | 3,43 % | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | 0,18 % | > > Ein Faktor 19 — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > Die Gegenprobe kostet eine Zeile: Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt \alpha nicht.
Ohne Formel gesagt: „Wenn du 5 % von A verkaufst und 5 % von B kaufst, hast du 10 % Umschlag und zahlst darauf Gebühren.“
Warum L_1 und nicht L_2? Die L_1-Norm ist konvex (also optimierbar) und erzeugt zusätzlich dünn besetzte Änderungen: Sie bevorzugt wenige große Umschichtungen gegenüber vielen kleinen. Das entspricht genau dem, was man in der Praxis will — nicht 50 Kleinstorders mit je 3 € Mindestgebühr.
-
In CVXPY schreibt man einfach cp.norm1(w - w_alt); intern wird das in lineare Hilfsvariablen zerlegt.
+
In CVXPY schreibt man einfach cp.norm1(w - w_alt); intern wird das in lineare Hilfsvariablen zerlegt.
Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: lambda_risk = 1.5 bedeutet dann effektiv eine Risikoaversion von 1{,}5/252 \approx 0{,}006. Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
+
Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: lambda_risk = 1.5 bedeutet dann effektiv eine Risikoaversion von 1{,}5/252 \approx 0{,}006. Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
Dieses Programm rechnet deshalb durchgehend auf Tagesbasis und annualisiert erst in der Ausgabe.
for s inrange(S): bedingungen.append(u[s] >=-R[s] @ w - gamma) # S einzelne Bedingungen
-
Beide beschreiben dasselbe Modell. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem Backtest mit 60 Rebalancings (Kapitel 21) summiert sich das erheblich.
-
Die allgemeine Regel: Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der Solver rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
+
Beide beschreiben dasselbe Modell. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem Backtest mit 60 Rebalancings (Kapitel 21) summiert sich das erheblich.
+
Die allgemeine Regel: Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der Solver rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
⚠️ Die Standardabweichung ist in CVXPY keine Wurzel
Im Vergleichsmodell optimiere_varianz() steht die Standardabweichung als
und nicht als das Naheliegende, cp.sqrt(cp.quad_form(w, sigma)). Der naheliegende Ausdruck ist nicht DCP und wird von CVXPY mit einem DCPError abgelehnt: cp.sqrt ist konkav und verlangt deshalb ein konkaves Argument, quad_form ist aber konvex. Die Regel wird verletzt, obwohl die Funktion als ganze mathematisch völlig harmlos ist — \sqrt{w^\top \Sigma w} ist konvex, DCP kann es nur nicht sehen.
+
und nicht als das Naheliegende, cp.sqrt(cp.quad_form(w, sigma)). Der naheliegende Ausdruck ist nicht DCP und wird von CVXPY mit einem DCPError abgelehnt: cp.sqrt ist konkav und verlangt deshalb ein konkaves Argument, quad_form ist aber konvex. Die Regel wird verletzt, obwohl die Funktion als ganze mathematisch völlig harmlos ist — \sqrt{w^\top \Sigma w} ist konvex, DCP kann es nur nicht sehen.
Wichtig ist die Fehlerdiagnose dahinter, weil sie leicht in die Irre geht: cp.psd_wrap(sigma) sieht nach der Lösung aus und ist keine. Es unterdrückt die numerische Beanstandung, dass \Sigma nicht als positiv semidefinit erkannt wird — gegen die strukturelle Regelverletzung im Aufbau hilft es nicht.
Der Ausweg ist immer derselbe: Zerlegen Sie \Sigma = LL^\top (Cholesky). Dann ist w^\top \Sigma w = \lVert L^\top w\rVert_2^2, und die Standardabweichung wird zur 2-Norm eines affinen Ausdrucks — konvex, regelkonform und für den Solver sogar die bessere Formulierung, weil sie direkt ein Kegelproblem ist. Der winzige Diagonalzuschlag 10^{-12} fängt den Fall ab, dass \Sigma numerisch nur halbdefinit ist (mehr Titel als Handelstage, duplizierte Spalten). Die vollständige Fehlertabelle steht in Anhang C.
-
💡 Zur Annualisierung von VaR und CVaR Ein häufiger Fehler skaliert den täglichen CVaR mit \sqrt{252}. Diese Wurzel-Zeit-Regel gilt streng nur für Standardabweichungen unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
+
💡 Zur Annualisierung von VaR und CVaR Ein häufiger Fehler skaliert den täglichen CVaR mit \sqrt{252}. Diese Wurzel-Zeit-Regel gilt streng nur für Standardabweichungen unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
Dieses Buch weist VaR und CVaR deshalb als Tageswerte aus und benennt die Skalierungsproblematik ausdrücklich. Wer Mehrtageshorizonte braucht, simuliert sie (Monte-Carlo aus Kapitel 12) statt zu skalieren.
Aufgabe 20.2 ⭐ — Warum kohärent? Erklären Sie in eigenen Worten, warum ein Risikomaß subadditiv sein sollte. Was bedeutet es wirtschaftlich, wenn diese Eigenschaft verletzt ist?
Aufgabe 20.3 ⭐⭐ — Rockafellar-Uryasev nachvollziehen. Zeigen Sie für die drei Szenarien mit Verlusten (1, 4, 9) und \alpha = 2/3: (a) Was ist der empirische CVaR? (b) Werten Sie \gamma + \frac{1}{S(1-\alpha)}\sum_s \max(\text{Verlust}_s-\gamma, 0) für \gamma \in \{0, 1, 4, 5, 9\} aus. (c) Bei welchem \gamma ist der Ausdruck minimal, und stimmt das Minimum mit (a) überein?
Aufgabe 20.4 ⭐⭐ — Einheiten prüfen. Angenommen, ein Modell verrechnet Jahresrendite gegen Tages-CVaR (siehe die Warnung in Abschnitt 20.6). Berechnen Sie: Welchem „effektiven“ täglichen \lambda entspräche lambda_risk = 1.5 dadurch? Welchen Wert müsste man stattdessen setzen, um dieselbe Wirkung wie RISIKOAVERSION = 1.5 im konsistenten Tagesmodell zu erzielen?
-
Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Variieren Sie RISIKOAVERSION von 0 bis 20 und tragen Sie Rendite, Volatilität, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
+
Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren. Variieren Sie RISIKOAVERSION von 0 bis 20 und tragen Sie Rendite, Volatilität, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
Aufgabe 20.6 ⭐⭐⭐ — Turnover-Grenze statt Strafe. Ersetzen Sie den Kostenterm durch eine harte Grenze\lVert\mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1 \le \tau und variieren Sie \tau \in \{0{,}05;\ 0{,}1;\ 0{,}25;\ 0{,}5;\ 1{,}0\}. (a) Wie verändert sich die erreichbare Rendite? (b) Was ist der Vorteil einer Grenze gegenüber einer Strafe — und was der Nachteil? (c) Wann würden Sie was einsetzen?
Danach können Sie: Eine Walk-Forward-Backtest-Architektur bauen, Lookahead-Bias vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
+
Danach können Sie: Eine Walk-Forward-Backtest-Architektur bauen, Lookahead-Bias vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
Lesen Sie diese Tabelle sehr genau. Die zugrunde liegenden Daten sind reiner Zufall — es gibt keinerlei Prognosekraft, kein Signal, keine Struktur. Trotzdem:
Lookahead erzeugt eine scheinbare Überrendite von 15,4 Prozentpunkten pro Jahr. Ein einziger falscher Index — iloc[t+1] statt iloc[t] — verwandelt Rauschen in eine scheinbar brillante Strategie.
-
Survivorship-Bias liefert 13,1 Prozentpunkte allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
+
Survivorship-Bias liefert 13,1 Prozentpunkte allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
Selbst die ehrliche Variante zeigt noch +1,1 Prozentpunkte — reines Rauschen, das aber ohne Weiteres als „leichte Outperformance“ verkauft werden könnte.
Die Lehre: Wenn Sie in einem Backtest eine Überrendite von 15 % sehen, ist die erste Frage nicht „Wie kann ich das nutzen?“, sondern „Wo ist mein Fehler?“. Und wenn Sie keinen finden, suchen Sie weiter — die Wahrscheinlichkeit, dass Sie tatsächlich eine Goldader entdeckt haben, ist deutlich kleiner als die, dass ein Index verrutscht ist.
@@ -25814,7 +25814,7 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
Drei Fehler, die dieser Teil verhindert
-
Bei Markowitz einsteigen. Wer Kapitel 18 überspringt, optimiert seine eigenen Schätzfehler und wundert sich über Gewichte, die niemand halten würde.
+
Bei Markowitz einsteigen. Wer Kapitel 18 überspringt, optimiert seine eigenen Schätzfehler und wundert sich über Gewichte, die niemand halten würde.
Den VaR für ein Risikomaß halten. Er sagt, ob die Schwelle gerissen wird, nicht wie schlimm es dahinter aussieht — und er ist nicht kohärent. Optimiert wird der CVaR.
Trägheit unterschätzen. Im Kraftwerkspark ist nicht die Prognosegüte das Problem, sondern dass sich ein Block mit acht Stunden Mindestlaufzeit um 18 Uhr nicht mehr herbeirufen lässt. Dieselbe Trägheit heißt im Depot Transaktionskosten.
@@ -25847,7 +25847,7 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
🚀 In 5 Minuten gelöst: Die Abnahmeprüfung, die in jedes Modell gehört
def pruefe_loesung(x, A, b, kosten, zielwert, ganzzahlig=(), toleranz=1e-6):
-"""Prueft eine Loesung gegen die Anforderungen - OHNE den Solver zu fragen."""
+"""Prueft eine Loesung gegen die Anforderungen - OHNE den [Solver](#gloss:solver){.glossar-link} zu fragen.""" beanstandungen = [] verbrauch = A @ x # 1. Werden Grenzen eingehalten?
@@ -25913,13 +25913,13 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
… die fünf typischen Praxisfallen benennen und je ein Gegenmittel nennen.
… ein unlösbares Modell in einen „am wenigsten schlechten“ Plan verwandeln.
… für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der Schattenpreis diese Frage nur für die nächste Einheit beantwortet.
… für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der Schattenpreis diese Frage nur für die nächste Einheit beantwortet.
… Zeitlimits und Optimalitätslücken sinnvoll setzen.
… die Architektur einer produktionsreifen OR-Plattform skizzieren.
… eine modellunabhängige Abnahmeprüfung schreiben, die eine Lösung gegen die Anforderungen prüft statt gegen das Modell.
… Status, Gap und Zeitausschöpfung protokollieren und begründen, warum eine konstante Laufzeit bei Zeitlimit ein Warnsignal ist.
-
… ein Modell in Domänenschicht, Modellbauer und Lösungs-DTO trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
+
… ein Modell in Domänenschicht, Modellbauer und Lösungs-DTO trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
… die Statuswerte verschiedener Solverbibliotheken auf eine gemeinsame Sprache abbilden.
@@ -25929,10 +25929,10 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
Abb. 22.1: Die fünf typischen Praxisfallen
Falle 1 — Infeasibility
-
Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE.
+
Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE.
Falsch: Das System stürzt ab oder zeigt „Fehler“. Der Disponent steht ohne Plan da.
-
Richtig:Hierarchische Relaxation. Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, wo es klemmt.
+
Richtig:Hierarchische Relaxation. Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, wo es klemmt.
#!/usr/bin/env python3
@@ -26048,11 +26048,11 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
print("="*78)
Falle 2 — Der Black-Box-Effekt
Ein Planer oder Trader lehnt ein System ab, wenn er die Zuweisungen nicht nachvollziehen kann. Akzeptanz ist keine Nebensache, sondern Voraussetzung für den Betrieb.
„Warum bekommt Person A die Stunde und nicht Person B?“ → „Weil B heute bereits zwei Vertretungsstunden hat und bei einer dritten die gesetzliche Ruhezeit von 11 Stunden unterschritten würde. A hat 80 Strafpunkte weniger.“
-
Bei Portfolioentscheidungen: Kostenzerlegung ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in Kapitel 7 und Kapitel 20).
+
Bei Portfolioentscheidungen: Kostenzerlegung ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in Kapitel 7 und Kapitel 20).
🎯 Die Faustregel für Erklärbarkeit Für jede Entscheidung sollten Sie drei Fragen beantworten können: 1. Warum diese Lösung? — Zielfunktionswert und seine Bestandteile. 2. Warum nicht die naheliegende Alternative? — Welche Bedingung verhindert sie? 3. Was würde die Lösung verbessern? — Schattenpreise: welcher Engpass, welcher Wert.
Kapitel 5 liefert für Frage 3 das Werkzeug. Nutzen Sie es — Schattenpreise sind in Managementgesprächen oft wertvoller als die Lösung selbst.
@@ -26166,14 +26166,14 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
Falle 3 — Regimewechsel und Schätzfehler
Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine Liquiditätskrise und keine Zinswende.
Harte Obergrenzen für Einzelpositionen und Sektoren — auch dann, wenn der Solver rechnerisch alles in einen Titel legen möchte. Die Grenze kostet Ertrag im Normalfall und rettet im Ernstfall.
Ausführlich in Kapitel 21 behandelt. Die Kurzfassung: Beide Fehler machen Backtests systematisch zu gut, ohne eine Fehlermeldung zu erzeugen. Der Lookahead-Selbsttest aus Abschnitt 21.5 findet den ersten automatisch.
Falle 5 — Laufzeitexplosion
-
Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
+
Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
@@ -26184,7 +26184,7 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
@@ -26216,11 +26216,11 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
22.4 Constraint Attribution: welche Bedingung kostet wie viel?
-
Der Constraint-Trace beantwortet „warum diese Zuweisung?“. Der Deletion Filter in Anhang C beantwortet „warum geht es gar nicht?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
+
Der Constraint-Trace beantwortet „warum diese Zuweisung?“. Der Deletion Filter in Anhang C beantwortet „warum geht es gar nicht?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
Das Modell rechnet. Der Plan ist zulässig. Und trotzdem ist er enttäuschend — nur weiß niemand, woran es liegt.
-
Die Antwort heißt Constraint Attribution: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit Kosten statt Zulässigkeit als Kriterium.
+
Die Antwort heißt Constraint Attribution: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit Kosten statt Zulässigkeit als Kriterium.
Gerechnet wird auf derselben Fabrik wie die Konfliktsuche in Anhang C, aber nach der Reparatur: Dort war die Lackierkapazität von 150 Stunden der Kern des Widerspruchs (2 \cdot 40 + 3 \cdot 30 = 170 > 150). Eine zweite Schicht hat sie auf 210 gebracht, der Mindestumsatz wurde auf 10 000 € gesenkt. Jetzt ist das Modell lösbar — und die Frage lautet nicht mehr „warum nicht?“, sondern „was kostet uns was?“.
#!/usr/bin/env python3
@@ -26628,7 +26628,7 @@ aus.
==============================================================================
Drei Befunde, und keiner steht im Solverergebnis
-
Erstens: bindend heißt nicht teuer. Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von 0,00 €. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den Erklaerbarkeit.py oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines LP.
+
Erstens: bindend heißt nicht teuer. Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von 0,00 €. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den Erklaerbarkeit.py oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines LP.
Zweitens: Der Schattenpreis ist eine Momentaufnahme. Er gilt für die nächste Einheit. Ein Planer kauft aber keine Einheit, sondern eine Schicht. Für die Lackiererei verspricht die Hochrechnung 4{,}00 \cdot 30 = 120 €; gemessen kommen 60 €. Der Preis von 4,00 € gilt exakt bis +15 Stunden, danach begrenzt etwas anderes — die zweite Hälfte der Sonderschicht wäre bezahlt und wirkungslos.
Und die Ranglisten drehen sich dabei um: Nach Schattenpreis führt der Trägervertrag mit 18,00 € je Stück, nach tatsächlicher Wirkung die Montage-Sonderschicht mit 450 €. Der Schattenpreis sagt, was eine Einheit wert ist — nicht, wie viele Einheiten zu haben sind. Für die Frage „welchen Hebel ziehen wir?“ ist das der entscheidende Unterschied.
Drittens: Ein Wunsch kann unmöglich statt teuer sein. Der Vertrieb hätte gern 55 Rahmen statt 47,5. Das Modell antwortet nicht mit einem Preis, sondern mit INFEASIBLE — und drei Bedingungen blockieren den Wunsch gemeinsam: Fällt eine davon weg, ist er erfüllbar, keine allein ist „der“ Grund. Genau die Struktur, die Anhang C für den unlösbaren Fall beschreibt, hier auf einen Wunsch angewandt.
Was Sie in der Abbildung sehen. Fünf der sechs Schichten bilden einen Weg: Daten gehen oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut sie am Ende auch so: als etwas, das man hinterher noch macht.
Fünf Prinzipien, die sich in der Praxis bewährt haben:
-
Snapshot-Prinzip. Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
-
Zustandslosigkeit. Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
-
Mensch in der Schleife. Der Optimierer schlägt vor, ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
-
Fallback-Strategie. Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
+
Snapshot-Prinzip. Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
+
Zustandslosigkeit. Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
+
Mensch in der Schleife. Der Optimierer schlägt vor, ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
+
Fallback-Strategie. Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
Versionierung. Modellversion, Parametersatz und Solver-Version gehören ins Protokoll jedes Laufs. Ohne das lässt sich nicht klären, warum das Ergebnis von letzter Woche anders aussah.
Wer or_kern nur für Statusauswertung und Prüfung benutzt, soll pandas nicht installieren müssen.
+
Wer or_kern nur für Statusauswertung und Prüfung benutzt, soll pandas nicht installieren müssen.
schattenpreise im DTO
@@ -27430,7 +27430,7 @@ Domaenenschicht.
22.7 Der Solverwechsel in der Praxis
Der vorige Abschnitt endet mit einer Behauptung: Ein Solverwechsel betreffe genau einen Baustein. Das ist die Art Satz, die in jedem Architekturvortrag vorkommt und die man deshalb nicht glauben sollte, bevor sie jemand vorgeführt hat.
-
Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: CP-SAT gegen HiGHS. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren (Kapitel 3).
+
Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: CP-SAT gegen HiGHS. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren (Kapitel 3).
Die Aufgabe ist eine kapazitierte Standortplanung: Aus sechs möglichen Lagern sind diejenigen zu eröffnen, mit denen zwölf Kunden am günstigsten beliefert werden. Jeder Kunde wird von genau einem Lager bedient, jedes eröffnete Lager kostet eine Fixgebühr, und die Kapazität reicht je Lager für 174 der insgesamt 387 Paletten — es müssen also mindestens drei Lager öffnen.
Der Aufbau folgt dem Muster aus or_kern.py:
@@ -27861,16 +27861,16 @@ ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).
22.8 Eine Checkliste vor dem Produktivgang
Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
-
Modell - [ ] Alle Nebenbedingungen sind dokumentiert — mit fachlicher Begründung, nicht nur als Code. - [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart sein? - [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall. - [ ] Big-M-Werte sind so klein wie möglich gewählt. - [ ] Die Zielfunktion ist in konsistenten Einheiten formuliert.
+
Modell - [ ] Alle Nebenbedingungen sind dokumentiert — mit fachlicher Begründung, nicht nur als Code. - [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart sein? - [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall. - [ ] Big-M-Werte sind so klein wie möglich gewählt. - [ ] Die Zielfunktion ist in konsistenten Einheiten formuliert.
Daten - [ ] Spaltenreihenfolge und Datentypen werden nach jedem Import geprüft. - [ ] Fehlende Werte und Ausreißer haben eine definierte Behandlung. - [ ] Kovarianzmatrizen werden auf positive Definitheit geprüft. - [ ] Datenstände sind versioniert und reproduzierbar.
Validierung - [ ] Das Ergebnis wurde gegen eine unabhängige Rechnung geprüft (Handrechnung, zweiter Solver, Simulation). - [ ] Alle Nebenbedingungen werden nach dem Lösen per assert verifiziert. - [ ] Es gibt einen Vergleich gegen eine naive Referenzstrategie. - [ ] Bei Backtests: Lookahead-Selbsttest bestanden, Zahl der Versuche protokolliert.
Betrieb - [ ] Zeitlimit und akzeptierter Gap sind festgelegt und begründet. - [ ] Es gibt eine Fallback-Strategie und Alarmierung. - [ ] Jedes Ergebnis ist mit Snapshot-ID, Modellversion und Solver-Version protokolliert. - [ ] Die Ergebnisse sind erklärbar (Kostenzerlegung, Schattenpreise, Constraint-Trace).
22.9 Weiterführende Literatur und Roadmap
-
Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Integer Programming — Branch-and-Cut, Schnittebenen, Formulierungsstärke.
+
Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Integer Programming — Branch-and-Cut, Schnittebenen, Formulierungsstärke.
Konvexe Optimierung * Boyd & Vandenberghe: Convex Optimization — Pflichtlektüre; frei verfügbar. Die theoretische Basis von CVXPY.
-
Constraint Programming und Scheduling * Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
-
Quantitative Finanzmathematik * López de Prado: Advances in Financial Machine Learning — Backtest-Overfitting, Denoising, Deflated Sharpe Ratio. * Cornuéjols, Peña & Tütüncü: Optimization Methods in Finance.
+
Constraint Programming und Scheduling * Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
+
Quantitative Finanzmathematik * López de Prado: Advances in Financial Machine Learning — Backtest-Overfitting, Denoising, Deflated Sharpe Ratio. * Cornuéjols, Peña & Tütüncü: Optimization Methods in Finance.
Wie es weitergeht
Der nächste Schritt nach diesem Kurs ist selten ein weiteres Verfahren, sondern die Verankerung im Betrieb: reproduzierbare Datenpipelines, versionierte Modelle, Monitoring der Lösungsqualität, bewusster Umgang mit Zeitlimits.
Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen:
@@ -27889,27 +27889,27 @@ ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).
Aufgabe 22.1 ⭐ — Hart oder weich, revisited. Nennen Sie für ein Vertretungsplanungssystem je zwei Bedingungen, die (a) zwingend hart bleiben müssen, (b) unbedingt weich sein sollten, (c) diskutabel sind. Begründen Sie.
-
Aufgabe 22.2 ⭐ — Zeitlimit wählen. Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-Rebalancing monatlich.
-
Aufgabe 22.3 ⭐⭐ — Relaxation einbauen. Nehmen Sie Ihr Modell aus der Aufgabe Eigener Dienstplan (Abschnitt 7.9) und machen Sie es INFEASIBLE-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
+
Aufgabe 22.2 ⭐ — Zeitlimit wählen. Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-Rebalancing monatlich.
+
Aufgabe 22.3 ⭐⭐ — Relaxation einbauen. Nehmen Sie Ihr Modell aus der Aufgabe Eigener Dienstplan (Abschnitt 7.9) und machen Sie es INFEASIBLE-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
Aufgabe 22.4 ⭐⭐ — Erklärbarkeit implementieren. Erweitern Sie ein beliebiges Modell aus dem Kurs um einen erklaere(loesung)-Report, der die drei Fragen aus Abschnitt 22.3 beantwortet.
Aufgabe 22.5 ⭐⭐ — Den Bericht in die Irre führen.Constraint_Attribution.py erzeugt seinen Bericht aus gerechneten Zahlen — richtig ist er deshalb noch nicht. (a) Setzen Sie den Deckungsbeitrag von Deckel von 9 € auf 20 €. Wie ändern sich Plan, Rangliste und Bericht? Welche Sätze bleiben wörtlich stehen, obwohl sie jetzt etwas anderes bedeuten? (b) Der Bericht nennt „Liefervertrag Träger” als teuerste Bindung. Formulieren Sie den Satz so um, dass ein Leser erkennt, worauf die Aussage beruht. (c) Welche Angabe müsste das Programm zusätzlich ausgeben, damit ein Empfänger die Belastbarkeit selbst einschätzen kann?
Aufgabe 22.6 ⭐⭐⭐ — Gap gegen Laufzeit. Nehmen Sie das MILP-Portfolio aus Kapitel 6 und vergrößern Sie es auf 50 Anlagen. Messen Sie Laufzeit und Zielwert bei Gap-Vorgaben von 10 %, 5 %, 2 %, 1 %, 0,1 % und 0 %. Stellen Sie den Zusammenhang grafisch dar und leiten Sie eine Empfehlung ab.
-
Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben. Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle in Abschnitt 5.7, die ungültige Kovarianzmatrix in Kapitel 11, die Sektor-Positionsindizes in Kapitel 19, die vermischten Einheiten in Abschnitt 20.6 oder die Rebalancing-Termine in Abschnitt 21.4) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
+
Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben. Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle in Abschnitt 5.7, die ungültige Kovarianzmatrix in Kapitel 11, die Sektor-Positionsindizes in Kapitel 19, die vermischten Einheiten in Abschnitt 20.6 oder die Rebalancing-Termine in Abschnitt 21.4) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
22.11 Finde den Denkfehler
-
Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts.
+
Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts.
🐛 Finde den Denkfehler: Das Modell, das seit einem Jahr nicht mehr optimiert
Ein Standortplanungsmodell geht 2024 in Betrieb. Es läuft jede Nacht, hat drei Sekunden Zeitbudget und liefert zuverlässig die Kosten des Tagesplans. Das Betriebsteam überwacht Abstürze und Laufzeitspitzen — beides tritt nie auf.
@@ -28218,7 +28218,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
1. Ihr Nachtjob läuft seit Monaten konstant 3,0 Sekunden bei einem Zeitlimit von 3 Sekunden. Was schließen Sie daraus? (a) Das System ist bemerkenswert stabil. (b) Das Zeitlimit greift jedes Mal. Der Solver bricht ab, statt fertig zu werden — die konstante Laufzeit ist das Symptom, nicht die Beruhigung. (c) Der Rechner ist ausgelastet und sollte aufgerüstet werden.
2. Eine Prüffunktion soll kontrollieren, ob ein Tourenplan die Fahrzeugkapazitäten einhält. Woher nimmt sie die geladenen Mengen? (a) Aus der Ladungsdimension des Routing-Modells — dort stehen sie ja bereits. (b) Sie summiert die Bedarfe der Kunden aus dem ausgegebenen Tourenplan auf. Alles andere fragt das Modell, ob es sich an sich selbst hält. (c) Aus der Zielfunktion, sofern die Kapazität dort bepreist ist.
-
3. Ein Modell meldet INFEASIBLE, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt? (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten statt Verboten —, damit der Solver benennt, welche Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
+
3. Ein Modell meldet INFEASIBLE, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt? (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten statt Verboten —, damit der Solver benennt, welche Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
22.13 Selbsttest
@@ -28237,7 +28237,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
22.14 Zusammenfassung
-
Infeasibility ist ein Entwurfsproblem, kein Solverproblem. Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
+
Infeasibility ist ein Entwurfsproblem, kein Solverproblem. Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
Erklärbarkeit entscheidet über Akzeptanz. Schattenpreise, Kostenzerlegung und Constraint-Traces sind keine Kür.
Bindend heißt nicht teuer. Eine Bedingung kann den Plan berühren und trotzdem 0,00 € kosten. Nur die kostenrelevanten lohnen eine Nachverhandlung.
Der Schattenpreis gilt für die nächste Einheit, nicht für die nächste Schicht. Hochgerechnet auf einen realistischen Schritt überschätzt er den Nutzen — im Beispiel um das Doppelte. Wer wissen will, was ein Hebel bringt, misst die Lockerung, statt zu multiplizieren; die beiden Ranglisten können sich dabei umdrehen.
@@ -28270,9 +28270,9 @@ sichtbar wird, und gibt Zeit zum Handeln.
23.1 In 5 Minuten gelöst
🚀 In 5 Minuten gelöst: drei Zeilen, die einen echten Fehler fangen
-
Die Schreinerei aus Kapitel 1, nur mit etwas knapperen Kapazitäten. Der Solver liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
+
Die Schreinerei aus Kapitel 1, nur mit etwas knapperen Kapazitäten. Der Solver liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}verbrauch_je_stueck = np.array([[3.0, 1.0], # Montagestunden je Tisch/Stuhl [6.0, 1.0]]) # Plattenmaterial je Tisch/Stuhl
@@ -28294,7 +28294,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: Plan [31. 48.] braucht [141. 234.], verfuegbar sind [140. 232.]
-
Und jetzt der Punkt. Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des LP. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
+
Und jetzt der Punkt. Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des LP. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
Bemerkenswert ist, was der Test prüft. Er prüft nicht, ob 30,67 das Optimum ist — das könnte er gar nicht, dafür bräuchte er einen zweiten Solver. Er prüft die Eigenschaft, die jede brauchbare Lösung haben muss: Sie hält die Kapazität ein. Diese Verschiebung — von der Frage nach dem richtigen Wert zur Frage nach den notwendigen Eigenschaften — ist der Schlüssel zum ganzen Kapitel.
🎯 Merksatz Ein Optimierungsmodell lässt sich fast nie gegen den richtigen Wert testen. Gegen seine Eigenschaften lässt es sich immer testen.
@@ -28685,7 +28685,7 @@ AssertionError: Plan [31. 48.] braucht [141. 234.], verfuegbar sind [140. 232.]<
@pytest.fixture(params=MODELLBAUER, ids=NAMEN)
-
Jeder Test läuft zweimal — einmal mit GLOP, einmal mit SciPy. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell
+
Jeder Test läuft zweimal — einmal mit GLOP, einmal mit SciPy. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell
test_beide_solver_liefern_dasselbe
@@ -29276,11 +29276,11 @@ NICHT ablesen darf man:
(Die genauen Prozentwerte schwanken von Lauf zu Lauf — es sind Verhältnisse zweier Zeitmessungen. Die Größenordnungen sind stabil.)
-
Bei OR-Tools gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren Solver sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau (Kapitel 3).
-
Die zweite unbequeme Zahl steht ganz rechts: CVXPY braucht rund 240 MB, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
+
Bei OR-Tools gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren Solver sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau (Kapitel 3).
+
Die zweite unbequeme Zahl steht ganz rechts: CVXPY braucht rund 240 MB, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
⚠️ Was diese Tabelle nicht sagt
-
Sie sagt nicht „ortools ist langsamer als scipy”. Gemessen wurde ein Problemtyp in einer Formulierung auf einer Maschine. Ein MILP statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
+
Sie sagt nicht „ortools ist langsamer als scipy”. Gemessen wurde ein Problemtyp in einer Formulierung auf einer Maschine. Ein MILP statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
Übertragbar ist der Trend, nicht der Absolutwert: dass der Aufbauanteil mit der Größe wächst, dass CVXPY einen konstanten Speichersockel hat. Der Zweck des Programms ist, dass Sie es auf Ihrer Maschine mit Ihrem Problem laufen lassen.
@@ -29709,12 +29709,12 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
================================================================================
💻 Code-Durchgang: drei Entscheidungen
-
1. Das Domänenmodell ist das API-Schema. Der Parameter heißt problem: Produktionsproblem — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus Kapitel 1 und antwortet bei Unsinn mit 422, bevor ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die or_kern.py schon beim Einlesen abfängt.
-
2. Die Antwort enthält Status und Gap, nicht nur Zahlen. Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das Loesung-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe DTO, das in Abschnitt 22.7 schon eine Prozessgrenze überlebt hat.
+
1. Das Domänenmodell ist das API-Schema. Der Parameter heißt problem: Produktionsproblem — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus Kapitel 1 und antwortet bei Unsinn mit 422, bevor ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die or_kern.py schon beim Einlesen abfängt.
+
2. Die Antwort enthält Status und Gap, nicht nur Zahlen. Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das Loesung-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe DTO, das in Abschnitt 22.7 schon eine Prozessgrenze überlebt hat.
3. Die Abnahmeprüfung läuft auch hier.pruefe_loesung() steht im Arbeiter, nicht nur im Skript. Ein Dienst ohne sie liefert Fehler schneller aus, als ein Mensch sie finden könnte.
Threads oder Prozesse? Eine Messung, keine Meinung
-
Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL während Solve() frei. Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
+
Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL während Solve() frei. Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
Das gilt aber nicht allgemein. Eine in reinem Python geschriebene Heuristik (Kapitel 9) hält den GIL die ganze Zeit — für sie braucht derselbe Dienst einen ProcessPoolExecutor. Die Entscheidung gehört gemessen, nicht angenommen.
⚠️ Der erste Entwurf dieser Messung war falsch
@@ -29822,16 +29822,16 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
INFEASIBLE um 3 Uhr nachts, niemand weiß warum
-
Relaxation mit gestaffelten Strafkosten (B18); Deletion Filter für die Diagnose
@@ -29975,8 +29975,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. Morgens um 7:15 Uhr meldet sich die dritte Lehrkraft krank. Die stellvertretende Schulleitung hat 20 Minuten, um einen Vertretungsplan zu erstellen, der Qualifikationen, Arbeitszeiten, bereits geleistete Vertretungen und persönliche Wünsche berücksichtigt.
Daten. Stundenplan (CSV: Klasse, Tag, Stunde, Fach, Lehrkraft, Raum) — in diesem Projektordner erzeugen die Skripte stundenplan*.py genau solche Dateien und eignen sich als Datenquelle. Zusätzlich: Qualifikationsmatrix, Deputate, Abwesenheitsmeldungen.
-
Modellskizze. * Variablen: x_{p,s} \in \{0,1\} — Person p übernimmt Slot s. * Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl Vertretungsstunden; Ruhezeiten. * Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche. * Solver: CP-SAT.
-
Abnahmekriterien. - [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots). - [ ] Liefert immer einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation). - [ ] Weist je Zuweisung eine Begründung aus. - [ ] Ausgabe als CSV und als lesbare Tagestabelle. - [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
+
Modellskizze. * Variablen: x_{p,s} \in \{0,1\} — Person p übernimmt Slot s. * Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl Vertretungsstunden; Ruhezeiten. * Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche. * Solver: CP-SAT.
+
Abnahmekriterien. - [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots). - [ ] Liefert immer einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation). - [ ] Weist je Zuweisung eine Begründung aus. - [ ] Ausgabe als CSV und als lesbare Tagestabelle. - [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
Stolperfallen. * Zu viele harte Regeln → INFEASIBLE an genau dem Tag, an dem man das System braucht. * Fairness nur über einen Tag statt über die Woche → dieselbe Person trifft es ständig. * Akzeptanz: Ohne Begründungsanzeige wird der Plan überschrieben.
Erweiterungen. Mehrtagesplanung; Raumkonflikte; Springerstunden; Schnittstelle zum Schulverwaltungsprogramm.
@@ -29985,9 +29985,9 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. 25 Pflegekräfte, drei Schichten täglich, 28-Tage-Zyklus. Es gelten Arbeitszeitgesetz, Tarifvertrag, Qualifikationsmix und individuelle Wünsche.
-
Modellskizze. * Variablen: x_{p,t,s} \in \{0,1\} — Person, Tag, Schicht. * Hart: Mindestbesetzung je Schicht und Qualifikationsmix (mindestens eine examinierte Kraft); nach Nachtschicht ≥ 2 freie Tage; höchstens 6 Arbeitstage in Folge; monatliche Sollstunden ± 10 %. * Weich: Wunschfrei, gleichmäßige Wochenendverteilung, ungeteilte Dienste, stabile Schichtfolgen (nicht Früh–Nacht–Früh). * Solver: CP-SAT mit Intervall- und Cumulative-Constraints.
-
Abnahmekriterien. - [ ] Alle gesetzlichen Regeln nachweislich eingehalten (per assert geprüft). - [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt (Gini-Koeffizient). - [ ] Läuft in unter 60 Sekunden. - [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
-
Stolperfallen. Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — Symmetriebrechung und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
+
Modellskizze. * Variablen: x_{p,t,s} \in \{0,1\} — Person, Tag, Schicht. * Hart: Mindestbesetzung je Schicht und Qualifikationsmix (mindestens eine examinierte Kraft); nach Nachtschicht ≥ 2 freie Tage; höchstens 6 Arbeitstage in Folge; monatliche Sollstunden ± 10 %. * Weich: Wunschfrei, gleichmäßige Wochenendverteilung, ungeteilte Dienste, stabile Schichtfolgen (nicht Früh–Nacht–Früh). * Solver: CP-SAT mit Intervall- und Cumulative-Constraints.
+
Abnahmekriterien. - [ ] Alle gesetzlichen Regeln nachweislich eingehalten (per assert geprüft). - [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt (Gini-Koeffizient). - [ ] Läuft in unter 60 Sekunden. - [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
+
Stolperfallen. Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — Symmetriebrechung und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
P3 — Tourenplanung für einen Lieferdienst
@@ -29995,7 +29995,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. Ein regionaler Lieferdienst fährt täglich 40–80 Adressen mit 4 Fahrzeugen an. Kunden haben Zeitfenster, Fahrzeuge Kapazitäten, Fahrer Arbeitszeiten.
Daten. Adressen aus einer CSV; Entfernungen über OpenStreetMap (osmnx, openrouteservice) oder als Luftlinie mit Umwegfaktor 1,3 als Näherung.
-
Modellskizze. OR-Tools Routing-Bibliothek mit Kapazitäts- und Zeitdimension; Metaheuristik GUIDED_LOCAL_SEARCH, Zeitlimit 30 Sekunden.
Abnahmekriterien. - [ ] Alle Kunden werden innerhalb ihrer Zeitfenster beliefert. - [ ] Vergleich gegen die bisherige manuelle Tourenplanung: Ersparnis in km und Minuten. - [ ] Kartendarstellung der Touren (folium). - [ ] Robust gegen Ausfall eines Fahrzeugs (Neuplanung in unter 30 s).
Stolperfallen. Luftlinie unterschätzt Fahrzeiten systematisch — mit realistischem Umwegfaktor arbeiten oder echte Routing-Distanzen holen. Zeitfenster, die physisch nicht erreichbar sind, führen zu „keine Lösung“ ohne Erklärung: Vorabprüfung einbauen (siehe Kapitel 8).
@@ -30005,14 +30005,14 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. Ein Handelsunternehmen prüft, welche von 12 möglichen Lagerstandorten eröffnet werden sollen, um 60 Filialen zu versorgen.
Modellskizze. Kombiniertes Standort- und Transportproblem: * y_j \in \{0,1\} — Lager j eröffnen (Fixkosten). * x_{ij} \ge 0 — Menge von Lager j zu Filiale i. * Kopplung: x_{ij} \le M\,y_j; Kapazität je Lager; Bedarfsdeckung je Filiale. * Ziel: Fixkosten + Transportkosten minimieren.
-
Abnahmekriterien. - [ ] Sensitivitätsanalyse: Wie verändert sich die Lösung bei ±20 % Transportkosten? - [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das? - [ ] Kartendarstellung mit Zuordnungslinien. - [ ] Amortisationsrechnung über 10 Jahre.
+
Abnahmekriterien. - [ ] Sensitivitätsanalyse: Wie verändert sich die Lösung bei ±20 % Transportkosten? - [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das? - [ ] Kartendarstellung mit Zuordnungslinien. - [ ] Amortisationsrechnung über 10 Jahre.
Stolperfallen. Big-M zu groß wählen (Kapitel 6) — hier ist die Lagerkapazität die natürliche Wahl. Fixkosten sind einmalig, Transportkosten laufend: Barwerte rechnen, nicht einfach addieren.
Ausgangslage. Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet Rüstzeit; die Rüstzeit hängt von der Reihenfolge ab.
+
Ausgangslage. Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet Rüstzeit; die Rüstzeit hängt von der Reihenfolge ab.
Modellskizze. Los- und Reihenfolgeplanung: * Variablen: Produktionsmengen, Rüstentscheidungen, Reihenfolge (Intervallvariablen). * Hart: Bedarfsdeckung je Periode, Maschinenkapazität, Mindestlosgrößen. * Ziel: Rüst- + Lager- + Fehlmengenkosten minimieren. * Solver: CP-SAT mit AddNoOverlap und reihenfolgeabhängigen Übergangszeiten.
Abnahmekriterien. - [ ] Gantt-Diagramm der Maschinenbelegung. - [ ] Vergleich gegen die aktuelle Praxis (Ersparnis in Rüststunden). - [ ] Schattenpreise: Welche Maschine ist der Engpass, was wäre eine zusätzliche Schicht wert?
@@ -30022,8 +30022,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
⚠️ Keine Anlageberatung. Dieses Projekt dient dem Methodenverständnis. Setzen Sie kein echtes Geld auf ein selbstgebautes Modell, dessen Grenzen Sie nicht vollständig verstehen.
Ausgangslage. Ein Privatdepot aus 10–15 ETFs und Einzeltiteln soll quartalsweise zurückgeführt werden — unter Berücksichtigung von Ordergebühren, Mindestordergrößen und der Steuerfreibetragsnutzung.
-
Modellskizze. * Variablen: Zielgewichte w_i; Binärvariablen für „Position wird gehandelt“. * Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, Kapitel 6), höchstens K Transaktionen je Rebalancing. * Ziel: erwartete Rendite − Risikoterm − Transaktionskosten − Steuerwirkung. * Solver: CVXPY (konvexer Teil) oder MIQP für die Kardinalität.
-
Abnahmekriterien. - [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt. - [ ] Alle Kosten (Ordergebühr, Spread, Steuer) sind explizit ausgewiesen. - [ ] Backtest über 5 Jahre mit Lookahead-Selbsttest. - [ ] Vergleich gegen: nie rebalancen, jährlich rebalancen, Gleichgewichtung. - [ ] Bericht, den ein Nicht-Fachmann versteht.
+
Modellskizze. * Variablen: Zielgewichte w_i; Binärvariablen für „Position wird gehandelt“. * Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, Kapitel 6), höchstens K Transaktionen je Rebalancing. * Ziel: erwartete Rendite − Risikoterm − Transaktionskosten − Steuerwirkung. * Solver: CVXPY (konvexer Teil) oder MIQP für die Kardinalität.
+
Abnahmekriterien. - [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt. - [ ] Alle Kosten (Ordergebühr, Spread, Steuer) sind explizit ausgewiesen. - [ ] Backtest über 5 Jahre mit Lookahead-Selbsttest. - [ ] Vergleich gegen: nie rebalancen, jährlich rebalancen, Gleichgewichtung. - [ ] Bericht, den ein Nicht-Fachmann versteht.
Stolperfallen. Steuern sind pfadabhängig (FIFO, Freibetrag) und passen nicht sauber in ein einperiodiges Modell — Näherung wählen und die Näherung dokumentieren. Und: Wenn Ihre Strategie die Gleichgewichtung nicht schlägt, ist das ein Ergebnis, kein Misserfolg.
P7 — Risikoreport mit CVaR und Stresstests
@@ -30031,16 +30031,16 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. Für ein bestehendes Portfolio soll ein monatlicher Risikobericht entstehen.
-
Inhalte des Berichts. * VaR und CVaR auf 95 % und 99 %, historisch und Monte-Carlo-simuliert. * Risikobeiträge je Position (marginal CVaR): Wer treibt das Risiko? * Stresstests: Was passiert bei −20 % Aktienmarkt, +200 Basispunkten Zins, Korrelationsanstieg auf 0,9? * Historische Szenarien nachspielen (2008, März 2020). * Konzentrationskennzahlen (Herfindahl-Index, effektive Titelzahl).
+
Inhalte des Berichts. * VaR und CVaR auf 95 % und 99 %, historisch und Monte-Carlo-simuliert. * Risikobeiträge je Position (marginal CVaR): Wer treibt das Risiko? * Stresstests: Was passiert bei −20 % Aktienmarkt, +200 Basispunkten Zins, Korrelationsanstieg auf 0,9? * Historische Szenarien nachspielen (2008, März 2020). * Konzentrationskennzahlen (Herfindahl-Index, effektive Titelzahl).
Abnahmekriterien. - [ ] Bericht als PDF, automatisch erzeugt. - [ ] Alle Kennzahlen mit zwei unabhängigen Methoden berechnet und verglichen. - [ ] Klartext-Zusammenfassung: „Im schlechtesten Prozent der Monate verlieren Sie typischerweise X €.“
-
Stolperfallen. Die Wurzel-Zeit-Regel bei CVaR (Kapitel 20) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
+
Stolperfallen. Die Wurzel-Zeit-Regel bei CVaR (Kapitel 20) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
P9 — Wenn der Solver aussteigt: Tourenplanung in Echtgröße
Ausgangslage. P3 hat funktioniert — mit 30 Kunden. Der Auftraggeber kommt mit 400 zurück, und derselbe Code liefert nach zwanzig Minuten noch keine Lösung. Das ist keine Panne, sondern der erwartbare Umschlagpunkt.
-
Modellskizze. Zwei Verfahren am selben Problem, verglichen unter gleichem Zeitbudget: * Exakt: MILP oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap. * Heuristisch: Startlösung mit einer Faustregel, dann Verbesserung durch Simulated Annealing oder Large Neighborhood Search (Kapitel 9). * Messgröße ist nicht „wer gewinnt”, sondern ab welcher Instanzgröße sich das Blatt wendet.
+
Modellskizze. Zwei Verfahren am selben Problem, verglichen unter gleichem Zeitbudget: * Exakt: MILP oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap. * Heuristisch: Startlösung mit einer Faustregel, dann Verbesserung durch Simulated Annealing oder Large Neighborhood Search (Kapitel 9). * Messgröße ist nicht „wer gewinnt”, sondern ab welcher Instanzgröße sich das Blatt wendet.
Abnahmekriterien. - [ ] Eine Tabelle über mindestens vier Instanzgrößen: exakt gegen heuristisch, jeweils Zielwert und Laufzeit. - [ ] Der Umschlagpunkt ist auf ±50 Kunden eingegrenzt und benannt. - [ ] Bei kleinen Instanzen wird gegen die bewiesen optimale Lösung geprüft — sonst weiß niemand, wie gut die Heuristik wirklich ist. - [ ] Fester Seed, reproduzierbarer Lauf; das Zugbudget ist fix, nicht die Uhrzeit.
⚠️ Die Falle, in die hier fast jeder tappt: die Heuristik nur auf großen Instanzen zu testen, wo man das Optimum nicht kennt. Dann sieht jede Lösung gut aus.
@@ -30052,7 +30052,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Ausgangslage. Die Geschäftsführung will „günstiger und grüner”. Beides zugleich gibt es nicht — und die übliche Antwort, beide Ziele mit Gewichten zu verrechnen, verdeckt genau die Frage, um die es geht.
Modellskizze. Ein Transport-, Beschaffungs- oder Produktionsmodell mit zwei Zielen: * Erst beide Ziele einzeln optimieren — das gibt die Eckpunkte und damit den Rahmen. * Dann die Front über das \varepsilon-Constraint-Verfahren abfahren (Kapitel 14): ein Ziel minimieren, das andere als Nebenbedingung schrittweise verschärfen. * Ergebnis ist keine Lösung, sondern eine Kurve — plus die Angabe, was jeder eingesparte Kilogramm CO₂ an Mehrkosten bedeutet.
-
Abnahmekriterien. - [ ] Die Pareto-Front ist berechnet und gezeichnet, nicht nur beschrieben. - [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet. - [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine gewichtete Summe niemals finden würde — und warum. - [ ] Die Entscheidungsvorlage nennt drei Punkte zur Auswahl, nicht dreißig.
+
Abnahmekriterien. - [ ] Die Pareto-Front ist berechnet und gezeichnet, nicht nur beschrieben. - [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet. - [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine gewichtete Summe niemals finden würde — und warum. - [ ] Die Entscheidungsvorlage nennt drei Punkte zur Auswahl, nicht dreißig.
P11 — Vom Skript zum Dienst: das Modell übergeben
@@ -30107,7 +30107,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
@@ -30142,9 +30142,9 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.1 Lösungen zu Kapitel „Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung“
1.1 — Analytik-Stufen. (a) deskriptiv (Vergangenheit beschreiben). (b) prädiktiv (Prognose). (c) präskriptiv (Handlungsanweisung unter Restriktionen). (d) prädiktiv (Klassifikation). (e) präskriptiv — hier kommt das begrenzte Budget ins Spiel, es muss zugeteilt werden. Beachten Sie das Paar (d)/(e): Die Prognose sagt, wer kündigen wird; die Optimierung sagt, wen man mit dem vorhandenen Geld halten kann.
-
1.2 — Hart oder weich? (a) hart — gesetzlich zwingend. (b) weich — Wunsch, mit Strafkosten. (c) hart — Patientensicherheit, rechtlich vorgeschrieben. (d) weich — Fairnessziel, über Strafterme. (e) hart, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
+
1.2 — Hart oder weich? (a) hart — gesetzlich zwingend. (b) weich — Wunsch, mit Strafkosten. (c) hart — Patientensicherheit, rechtlich vorgeschrieben. (d) weich — Fairnessziel, über Strafterme. (e) hart, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
1.3 — Kombinatorik. (a) 12! = 479\,001\,600. (b) 479\,001\,600 / 5\cdot10^6 \approx 95{,}8 Sekunden \approx 1{,}6 Minuten. (c) 13! = 6\,227\,020\,800; das sind 1245 s \approx 20{,}8 Minuten — Faktor 13. Jeder weitere Auftrag multipliziert die Zeit mit der neuen Anzahl. (d) Eine Stunde = 3600 s \to 1{,}8\cdot10^{10} Prüfungen. 13! = 6{,}2\cdot10^9 ✓, 14! = 8{,}7\cdot10^{10} ✗. Also 13 Aufträge.
-
1.4 — Modell lesen. (a) Variablen x_1, x_2 \ge 0; Parameter (3,5) und die Kapazitäten (4,12,18); Zielfunktion \max 3x_1+5x_2; vier Nebenbedingungen inkl. Nichtnegativität. (b) (2,6): 2\le4 ✓, 12\le12 ✓, 6+12=18\le18 ✓ → zulässig, Z=36. (4,3): 4\le4 ✓, 6\le12 ✓, 12+6=18\le18 ✓ → zulässig, Z=27. (c) Beste ganzzahlige Lösung ist (2,6) mit Z=36 — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
+
1.4 — Modell lesen. (a) Variablen x_1, x_2 \ge 0; Parameter(3,5) und die Kapazitäten (4,12,18); Zielfunktion\max 3x_1+5x_2; vier Nebenbedingungen inkl. Nichtnegativität. (b) (2,6): 2\le4 ✓, 12\le12 ✓, 6+12=18\le18 ✓ → zulässig, Z=36. (4,3): 4\le4 ✓, 6\le12 ✓, 12+6=18\le18 ✓ → zulässig, Z=27. (c) Beste ganzzahlige Lösung ist (2,6) mit Z=36 — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
1.5 — Bäckerei.
from ortools.sat.python import cp_modelm = cp_model.CpModel()
@@ -30157,8 +30157,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
s = cp_model.CpSolver(); s.Solve(m)
Zwei lehrreiche Beobachtungen: 1. Die Vertragsbedingung x_1 \ge 40 ist nicht bindend — der Solver backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch. 2. Das kontinuierliche Optimum (40;\ 116{,}67) liefert denselben Zielwert 450. Das Problem hat also mehrere optimale Lösungen — die Zielfunktion verläuft parallel zur Mehlrestriktion (2{,}5/0{,}5 = 5 = 3{,}0/0{,}6). Prüfen Sie das nach: Genau deshalb ist hier auch die ganzzahlige Lösung ohne Verlust erreichbar.
-
1.6 — Sensitivität durch Ausprobieren. Schleife über RAM_GESAMT in range(54, 73, 2), jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die vCPU-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der Schattenpreis (Kapitel 5). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
+
Zwei lehrreiche Beobachtungen: 1. Die Vertragsbedingung x_1 \ge 40 ist nicht bindend — der Solver backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch. 2. Das kontinuierliche Optimum (40;\ 116{,}67) liefert denselben Zielwert 450. Das Problem hat also mehrere optimale Lösungen — die Zielfunktion verläuft parallel zur Mehlrestriktion (2{,}5/0{,}5 = 5 = 3{,}0/0{,}6). Prüfen Sie das nach: Genau deshalb ist hier auch die ganzzahlige Lösung ohne Verlust erreichbar.
+
1.6 — Sensitivität durch Ausprobieren. Schleife über RAM_GESAMT in range(54, 73, 2), jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die vCPU-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der Schattenpreis (Kapitel 5). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
1.7 — Eigenes Problem. Individuell. Prüfkriterien: Sind die Variablen wirklich entscheidbar (nicht bereits festgelegt)? Hat die Zielfunktion eine Einheit? Ist jede harte Bedingung wirklich unverhandelbar?
Finde den Denkfehler — Die Schreinerei verdoppelt ihren Gewinn
@@ -30195,7 +30195,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
2.1 — Matrixform lesen.\max 4x_1 + x_2 + 6x_3 u. d. N. x_1 + 2x_2 \le 10, x_2 + 3x_3 \le 12, x \ge 0. 3 Variablen, 2 Nebenbedingungen (plus Nichtnegativität).
2.2 — Standardform.\min -7x_1 + 2x_2 u. d. N. -4x_1 - x_2 \le -20 (aus „\ge“ durch Multiplikation mit -1), x_1 - x_2 \le 3und-x_1 + x_2 \le -3 (Gleichung als zwei Ungleichungen), x_1, x_2 \ge 0.
2.3 — Ecken von Hand. (b) Ecken: (0,0), (6,0), (4,4), (0,8). (c) Z: 0, 12, 20, 24 → Optimum (0,8) mit Z = 24. (d) Bei \max 2x_1+2x_2: Z(4,4) = 16, Z(0,8) = 16 — die Zielfunktion ist parallel zur Kantex_1+x_2=8. Es gibt dann unendlich viele optimale Lösungen (die ganze Kante), aber weiterhin mindestens eine in einer Ecke — der Fundamentalsatz bleibt gültig.
-
2.4 — Konvexität. (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex (f'' = 12x^2 \ge 0). (c) nicht konvex, sondern konkav (f'' = -\tfrac14 x^{-3/2} < 0). (d) nicht konvex, konkav (f'' = -1/x^2 < 0). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix 2\mathbf{I} \succ 0). (f) konvexe Menge (Kreisscheibe). (g) konvexe Menge — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die Funktion1/x ist konvex, und die Menge \{x_2 \ge 1/x_1\} ist der Epigraph einer konvexen Funktion, also konvex).
+
2.4 — Konvexität. (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex (f'' = 12x^2 \ge 0). (c) nicht konvex, sondern konkav (f'' = -\tfrac14 x^{-3/2} < 0). (d) nicht konvex, konkav (f'' = -1/x^2 < 0). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix 2\mathbf{I} \succ 0). (f) konvexe Menge (Kreisscheibe). (g) konvexe Menge — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die Funktion1/x ist konvex, und die Menge \{x_2 \ge 1/x_1\} ist der Epigraph einer konvexen Funktion, also konvex).
2.6 — Bäckerei visualisieren. Ecken: (40,0), (150,0), (40,116{,}67) und der Schnittpunkt von Mehl- und Ofengrenze. Beste Ecke ist (40;\ 116{,}67) mit Z = 450 (im kontinuierlichen Fall).
2.7 — Eigenwert-Clipping.
@@ -30205,7 +30205,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Für \mathbf{P}_2 ergibt sich eine PSD-Matrix mit Korrelation exakt 1{,}0 — das Verfahren zieht die unmögliche Korrelation auf den nächstgelegenen zulässigen Wert. Für Kovarianzmatrizen setzt man in der Praxis auf einen kleinen positiven Wert statt auf 0 (np.maximum(lam, 1e-10)), damit die Matrix invertierbar bleibt.
Finde den Denkfehler — Die unauffällige Transposition
-
Warum nichts auffällt.\mathbf{A} ist quadratisch, also passen die Dimensionen auch transponiert. NumPy prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges LP vorgelegt und löst es korrekt — nur eben ein anderes. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos aussieht: Wäre die Matrix 2 \times 3 gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
+
Warum nichts auffällt.\mathbf{A} ist quadratisch, also passen die Dimensionen auch transponiert. NumPy prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges LP vorgelegt und löst es korrekt — nur eben ein anderes. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos aussieht: Wäre die Matrix 2 \times 3 gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
Der Plan an den echten Restriktionen. Mit \mathbf{A} = \begin{pmatrix} 1 & 2 \\ 3 & 1\end{pmatrix} und \mathbf{x} = (5{,}6;\ 0{,}8):
@@ -30220,7 +30220,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Vorbeugend hilft außerdem, Matrizen nie positionsweise abzutippen, sondern über benannte Spalten aus einer Tabelle zu erzeugen — genau das tut Excel_Bruecke.py aus Kapitel 1.
Micro-Quiz
1 — (b). Der Fundamentalsatz sagt nur, dass ein Optimum in einer Ecke angenommen wird. (a) ist falsch, weil es mehrere optimale Ecken geben kann (wenn die Zielfunktion parallel zu einer Kante verläuft) und dann sogar unendlich viele optimale Punkte auf der Verbindungsstrecke. (c) ist falsch, weil die Zahl der Eckenkandidaten kombinatorisch wächst: Bei 6 Variablen und 4 Ungleichungen plus 6 Nichtnegativitäten sind \binom{10}{6} = 210 Systeme zu prüfen — bei 50 Variablen wären es 10^{29}. Der Satz sagt, wo man suchen muss, nicht dass die Suche billig ist.
-
2 — (b). Faustregel \kappa = 10^{k} ⟹ etwa k signifikante Stellen verloren; bei 10^{11} bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die Konditionszahl mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
+
2 — (b). Faustregel \kappa = 10^{k} ⟹ etwa k signifikante Stellen verloren; bei 10^{11} bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die Konditionszahl mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
3 — (c).int() schneidet ab: int(0.99999998) == 0 macht aus einer Ja- eine Nein-Entscheidung (a). Den Wert unverändert weiterzureichen (b) verschiebt das Problem nur in die nachgelagerte Verarbeitung, wo dann irgendwann doch jemand int() schreibt. Richtig ist die Prüfung gegen die Toleranz mit Fehlermeldung im Zweifelsfall — die Funktion sichere_ganzzahl() aus Skalierung_Kondition.py.
Selbsttest
@@ -30232,18 +30232,18 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.3 Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“
-
3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) MILP über highspy oder CP-SAT (Standortproblem mit Fixkosten). (f) scipy.optimize.minimize mit Multistart (nicht konvex).
+
3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) MILP über highspy oder CP-SAT (Standortproblem mit Fixkosten). (f) scipy.optimize.minimize mit Multistart (nicht konvex).
3.2 — Bäckerei viermal. Alle vier müssen Z = 448 (ganzzahlig) bzw. 450 (kontinuierlich) liefern. Denken Sie an die Prozesstrennung, falls ortools und highspy kollidieren.
-
3.3 — CSR-Format.values = [3, 1, 2, 5, 4, 6], indices = [0, 3, 2, 0, 1, 3], starts = [0, 2, 3]. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte 3\times4 = 12. Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
+
3.3 — CSR-Format.values = [3, 1, 2, 5, 4, 6], indices = [0, 3, 2, 0, 1, 3], starts = [0, 2, 3]. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte 3\times4 = 12. Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
3.4 — Konvexitätsprüfung.\min x^3 wirft DCPError: Problem does not follow DCP rules, weil x^3 auf [-2,2] weder konvex noch konkav ist. \min x^2 läuft und liefert x=0. Der Unterschied: CVXPY akzeptiert nur Ausdrücke, deren Konvexität es beweisen kann — dafür garantiert es das globale Optimum.
3.5 — Laufzeitvergleich. Erwartetes Muster: linprog und highspy liegen bei kleinen Modellen gleichauf (Overhead dominiert); ab etwa n \gtrsim 500 zieht highspy davon, weil der Modellaufbau effizienter ist. CVXPY hat den größten festen Aufwand (Ausdrucksbaum-Kompilierung), der bei wiederholten Läufen mit cp.Parameter teilweise entfällt.
-
3.6 — Eigene Entscheidungshilfe. Ergänzungen: Bei kommerzieller Lizenz Gurobi/CPLEX über Pyomo oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo (algebraische Notation, Trennung von Daten und Modell).
+
3.6 — Eigene Entscheidungshilfe. Ergänzungen: Bei kommerzieller Lizenz Gurobi/CPLEX über Pyomo oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo (algebraische Notation, Trennung von Daten und Modell).
Finde den Denkfehler — Der Solver, der angeblich dreimal schneller ist
Was da alles mitgemessen wird. Die Stoppuhr läuft ab der ersten Zeile, also mindestens über drei Dinge, die mit Lösegeschwindigkeit nichts zu tun haben:
-
Der import. CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. linprog steht in einem Prozess, der SciPy ohnehin schon geladen hat, praktisch sofort bereit.
+
Der import. CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. linprog steht in einem Prozess, der SciPy ohnehin schon geladen hat, praktisch sofort bereit.
Der Modellaufbau. CVXPY baut einen Ausdrucksbaum und kompiliert ihn in die Standardform des Solvers. Das ist echter Aufwand — aber Aufbau, nicht Rechnen.
Die Reihenfolge. CVXPY läuft zuerst und bezahlt dabei alles, was danach im Betriebssystem-Cache liegt: Bibliotheken, Speicherseiten, JIT-Wärme. Tauschen Sie die beiden Blöcke, und die Zahlen verschieben sich allein deshalb.
@@ -30273,14 +30273,14 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Micro-Quiz
1 — (b) CP-SAT. Es geht um diskrete Zuweisung mit logischen Regeln („nicht gleichzeitig“) — CP-SATs Kerngebiet, und Regeln dieser Art formuliert man dort direkt statt über Big-M-Umwege. (a) scheidet aus, weil CVXPY keine sinnvolle Ganzzahligkeit in dieser Größenordnung bietet; (c) findet bei einem diskreten Problem bestenfalls ein lokales Optimum und hat keinerlei Handhabe für „entweder–oder“-Regeln.
2 — (c) Modellaufbau vektorisieren. 32 von 40 Sekunden fallen an, bevor der Solver startet. Ein kommerzieller Solver (a) beschleunigt bestenfalls die verbleibenden 8 Sekunden — selbst bei Faktor 4 gewinnen Sie 6 der 40 Sekunden. Das Zeitlimit (b) betrifft den Abbruch, nicht die Geschwindigkeit. Die Regel dahinter: erst messen, wo die Zeit hingeht, dann optimieren.
-
3 — (b). Alle vier sind Modellierungsschichten über HiGHS. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver (Kapitel 11).
+
3 — (b). Alle vier sind Modellierungsschichten über HiGHS. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver (Kapitel 11).
Selbsttest
Modellierungsschicht (Python) und Solver-Schicht (C++). Die Trennung erlaubt Solvertausch ohne Modelländerung.
CVXPY prüft die Konvexität und lehnt ab, was es nicht garantieren kann; minimize prüft nichts und liefert ein lokales Optimum.
Werte, Spaltenindizes und Zeilenstartpositionen der Nicht-Null-Einträge. Entscheidend, weil reale Modelle extrem dünn besetzt sind.
Wegen des Kompilierungsaufwands des Ausdrucksbaums. Kein Argument dagegen, weil dieser Aufwand einmalig ist, die Lesbarkeit hoch und die Konvexitätsprüfung wertvoll.
-
Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die Routing-Bibliothek überlegen).
+
Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die Routing-Bibliothek überlegen).
A.4 Lösungen zu Kapitel „Vom Management-Wunsch zum Modell“
@@ -30363,7 +30363,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
5.1 — Schlupf deuten. (a) Ressourcen 1 und 3 (Schlupf 0). (b) Ja: Überall ist s_i \cdot y_i = 0. (c) In Ressource 3 — der höchste Schattenpreis (9,8) bedeutet den größten Grenznutzen.
5.2 — Vorzeichen. Er hat die Zielfunktion für linprog negiert und die Dualwerte nicht zurückgedreht. Der korrekte Schattenpreis ist +45 €.
5.3 — Simplex von Hand. Starttableau mit s_1, s_2 in der Basis. Erste Iteration: Pivotspalte x_1 (-5), Quotienten 24/6 = 4 und 6/1 = 6 → Pivotzeile 1. Nach dem Tausch: x_1 = 4, Z = 20. Zweite Iteration: Pivotspalte x_2, Pivotzeile 2 → x_1 = 3, x_2 = 1{,}5, Z = 21. Optimum:\mathbf{x}^* = (3;\ 1{,}5), Z^* = 21, Schattenpreise y^* = (0{,}75;\ 0{,}50).
5.5 — Unbeschränktheit. Der Solver wirft „Problem ist unbeschränkt“. Geometrisch: Der zulässige Bereich \{x_1 - x_2 \le 5,\ x \ge 0\} ist nach oben offen — man kann x_1 und x_2 gemeinsam beliebig wachsen lassen (z. B. x_1 = x_2 = t für t \to \infty), ohne eine Bedingung zu verletzen, und Z = 2t wächst mit. In der Praxis fehlt fast immer eine Kapazitätsgrenze.
5.6 — Gültigkeitsbereich. (a) Bei ca. 66,7 Stunden Prüfkapazität wird die Lackierzeit zum Engpass; der Schattenpreis der Prüfung fällt dann. (b) Zukaufen lohnt bis zu dem Punkt, an dem der Schattenpreis unter 18 €/h fällt. (c) Der Gewinnverlauf ist stückweise linear und konkav: Jedes Teilstück hat die Steigung des jeweils gültigen Schattenpreises, und die Steigungen werden immer flacher — jede zusätzliche Einheit bringt weniger, weil andere Engpässe nachrücken.
5.7 — Phase 1. Ansatz: Für jede Zeile mit b_i < 0 (nach Umformung zu \ge) eine künstliche Variable a_i \ge 0 einführen, Hilfszielfunktion \min \sum a_i lösen. Ist das Minimum 0, existiert eine zulässige Basislösung, und man startet Phase 2 mit dem erreichten Tableau. Ist es > 0, ist das Problem unzulässig. Für das Beispiel: Optimum (12;\ 0), Z = 36.
@@ -30445,7 +30445,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Das ist die allgemeine Regel für jede Sensitivitätsaussage über mehr als eine Grenzeinheit: Der Schattenpreis zeigt die Richtung, die Neuberechnung liefert den Betrag. Bei einer Investitionsentscheidung kostet ein zusätzlicher Solverlauf Millisekunden — und hier hätte er 380 000 € gespart.
Micro-Quiz
1 — (b). Genau der Satz vom komplementären Schlupf: s_i \cdot y_i = 0. Ist Schlupf vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil verkehrt; (c) verwechselt Schlupf mit Auslastung — s_i = 12 heißt gerade, dass 12 Einheiten übrig sind.
-
2 — (b) Entartung. 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
+
2 — (b) Entartung. 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
3 — (b). Der Schlupf ist Solver-Rauschen in der Größenordnung der Maschinengenauigkeit — rechnerisch null, aber nicht == 0.0. Richtig ist abs(schlupf) < 1e-7. (a) unterstellt dem Solver einen Fehler, den er nicht gemacht hat; (c) ist frei erfunden — Schlupfwerte sind bei korrekt aufgestelltem Modell nie negativ, abgesehen von genau diesem Rauschen.
Selbsttest
@@ -30458,9 +30458,9 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.6 Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“
6.1 — Runden widerlegen. Beispiel: \max x_1 + x_2 u. d. N. 10x_1 + 10x_2 \le 15, ganzzahlig. LP-Optimum Z = 1{,}5; Abrunden ergibt (0,0) mit Z = 0 — 100 % Verlust. Das ganzzahlige Optimum ist (1,0) mit Z = 1. Prinzip: kleine Zahlen plus knappe Kapazität.
-
6.2 — Big-M wählen.M = 250 (die bekannte Kapazität). Bei M = 10^6 bleibt das Modell korrekt, aber die LP-Relaxation wird extrem schwach: y_j darf schon bei x_j/10^6 liegen, die Schranke ist praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
+
6.2 — Big-M wählen.M = 250 (die bekannte Kapazität). Bei M = 10^6 bleibt das Modell korrekt, aber die LP-Relaxation wird extrem schwach: y_j darf schon bei x_j/10^6 liegen, die Schranke ist praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
6.4 — Branch-and-Bound. LP-Relaxation der Wurzel: nach Nutzen/Gewicht sortieren (8/5=1{,}6; 11/7=1{,}57; 6/4=1{,}5; 4/3=1{,}33). Gierig füllen: x_1=1 (Rest 9), x_2=1 (Rest 2), x_3 = 0{,}5 → Z_{LP} = 8+11+3 = 22. Verzweigen über x_3. Ast x_3=0: x_1=1,x_2=1,x_4=2/3 → Z = 21{,}67; weiter verzweigen → beste ganzzahlige Lösung (1,1,0,0) mit Z=19. Ast x_3=1: x_1=1, x_3=1, Rest 5 → x_2=5/7 → Z = 21{,}86; verzweigen führt auf (1,0,1,1) mit Z = 18 und (0,1,1,0) mit Z=17. Optimum: (1,1,0,0), Z = 19.
+
6.4 — Branch-and-Bound. LP-Relaxation der Wurzel: nach Nutzen/Gewicht sortieren (8/5=1{,}6; 11/7=1{,}57; 6/4=1{,}5; 4/3=1{,}33). Gierig füllen: x_1=1 (Rest 9), x_2=1 (Rest 2), x_3 = 0{,}5 → Z_{LP} = 8+11+3 = 22. Verzweigen über x_3. Ast x_3=0: x_1=1,x_2=1,x_4=2/3 → Z = 21{,}67; weiter verzweigen → beste ganzzahlige Lösung (1,1,0,0) mit Z=19. Ast x_3=1: x_1=1, x_3=1, Rest 5 → x_2=5/7 → Z = 21{,}86; verzweigen führt auf (1,0,1,1) mit Z = 18 und (0,1,1,0) mit Z=17. Optimum: (1,1,0,0), Z = 19.
6.5 — Kardinalität variieren. (a) Ab K = 3 steigt der Ertrag nicht mehr wesentlich, weil bereits drei Positionen à 40 000 € das Budget von 100 000 € abdecken können. (b) Rechenzeit steigt zunächst (mehr Kombinationen), fällt bei großem K wieder (Restriktion bindet nicht mehr). (c) Der faire Preis ist die Differenz der Netto-Erträge zwischen K=3 und K=4 — bei den gegebenen Daten nahe null, weil die Obergrenze von 40 000 € bereits bindet.
6.6 — Big-M-Effekt. Erwartetes Muster: Knotenzahl und Laufzeit steigen deutlich mit M. Bei M = 10^9 ist die Relaxation so schwach, dass der Solver kaum noch prunen kann.
6.7 — Standortplanung. Modell wie in Projekt P4. Prüfen Sie: Gesamtkapazität der eröffneten Lager \ge Gesamtbedarf (140); bei Kapazität 80 je Lager sind mindestens \lceil 140/80 \rceil = 2 Lager nötig.
@@ -30494,8 +30494,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Vorbeugend gilt weiter die Regel aus dem Kapitel: M so klein wie möglich, hergeleitet aus einer echten Kapazität. Hier wäre das schlicht die Lagerkapazität — mehr kann ein Lager ohnehin nicht ausliefern.
Micro-Quiz
1 — (b). Der Gap (48\,200 - 47\,100)/48\,200 = 2{,}3\,\% ist eine Garantie, keine Schätzung: Besser als 47 100 € kann es nachweislich nicht werden, also ist der vorliegende Plan höchstens 2,3 % zu teuer. (a) wirft eine völlig brauchbare Lösung weg — genau der Fehler, den if status == OPTIMAL: ... else: return None produziert. (c) verwechselt die Schranke mit einem erreichbaren Wert: 47 100 € ist eine untere Schranke, es ist völlig offen, ob ein Plan mit diesen Kosten überhaupt existiert.
-
2 — (b). Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der Trickle Flow dagegen ist still: Das Modell meldet Optimal, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
-
3 — (c). Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und INFEASIBLE bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen Warm-Start-Effekt, den es gar nicht gibt.
+
2 — (b). Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der Trickle Flow dagegen ist still: Das Modell meldet Optimal, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
+
3 — (c). Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und INFEASIBLE bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen Warm-Start-Effekt, den es gar nicht gibt.
Selbsttest
Weil die Relaxation mehr Lösungen zulässt (alle ganzzahligen plus gebrochene) — das Maximum über einer größeren Menge ist mindestens so groß.
@@ -30509,7 +30509,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
7.1 — Propagation. Aus x_1 + x_2 = 8 und x_1 < x_2 folgt x_1 < 4, also x_1 \in \{2,3\} (denn x_2 = 8-x_1 \le 6 verlangt x_1 \ge 2) und entsprechend x_2 \in \{5,6\}. Aus 36 Kombinationen werden 2 zulässige.
7.2 — Hart oder weich. (a) hart. (b) weich, mittlere Strafe (~50). (c) hart. (d) weich, mittlere Strafe (~80, weil geteilte Dienste stark belasten). (e) hart, falls gesetzlich; sonst weich mit sehr hoher Strafe (~1000).
7.3 — Regeln ergänzen. (a) for s in (2,3): modell.Add(x["Frau_Albrecht", s] == 0) (b) Hilfsvariablen arbeitet_bauer, arbeitet_koch per AddMaxEquality an die Zuweisungssummen koppeln, dann modell.Add(arbeitet_bauer + arbeitet_koch <= 1). (c) Belohnung = negative Strafe: strafterme.append(-30 * folge_var), wobei folge_var per AddBoolAnd/OnlyEnforceIf an x_{p,0} \wedge x_{p,1} gekoppelt wird.
-
7.4 — Infeasibility. Mit MAX_VERTRETUNGEN = 0 meldet der Solver INFEASIBLE. Nach Einbau der Schlupfvariablen lässt der Solver alle vier Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei MAX_VERTRETUNGEN = 1: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
+
7.4 — Infeasibility. Mit MAX_VERTRETUNGEN = 0 meldet der Solver INFEASIBLE. Nach Einbau der Schlupfvariablen lässt der Solver alle vier Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei MAX_VERTRETUNGEN = 1: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
7.5 — Sudoku.
x = [[m.NewIntVar(1, 9, f"x{i}{j}") for j inrange(9)] for i inrange(9)]for i inrange(9): m.AddAllDifferent(x[i]) # Zeilen
@@ -30543,7 +30543,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
-
Nur die Kombination trägt. (c) Bei 15 Aufträgen (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet FEASIBLE mit Makespan 200; acht Arbeiter melden OPTIMAL mit demselben Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im Beweis, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
+
Nur die Kombination trägt. (c) Bei 15 Aufträgen (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet FEASIBLE mit Makespan 200; acht Arbeiter melden OPTIMAL mit demselben Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im Beweis, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
7.7 — Job-Shop erweitern. (a) Rüstzeiten: AddNoOverlap durch paarweise Disjunktionen mit Übergangszeit ersetzen, oder AddCircuit je Maschine mit Übergangsmatrix. (b) Verspätung: tardiness = MaxEquality(0, ende - faellig), in die Zielfunktion. (c) AddCumulative(intervalle, [1]*n, 2) statt AddNoOverlap für die Doppelmaschine.
7.8 — Wochendienstplan. Siehe Projekt P2. Kernpunkte: Nachtschicht-Folgeregel über AddImplication; „höchstens 5 Tage in Folge“ über gleitende Fenster (sum(x[p,t..t+5]) <= 5).
Finde den Denkfehler — Die Betriebsvereinbarung, die niemanden interessiert
@@ -30615,7 +30615,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Micro-Quiz
1 — (b) wirkungslos.INFEASIBLE ist kein Abbruch, sondern ein Beweis: CP-SAT hat gezeigt, dass keine zulässige Lösung existiert. Mehr Zeit (a) oder mehr Arbeiter (c) ändern daran nichts — sie bestätigen dasselbe Ergebnis nur schneller. Der Unterschied zu UNKNOWN ist genau dieser: Dort wurde nichts gefunden, hier gibt es nichts. Die Ursache liegt in den harten Regeln; Anhang C zeigt, wie man die widersprüchliche Teilmenge isoliert.
2 — (b).AddNoOverlap ist kompakter und propagiert stärker, weil der spezialisierte Propagator alle Intervalle gemeinsam betrachtet statt paarweise. (a) ist falsch: Beide Formulierungen beschreiben dieselbe Menge zulässiger Lösungen, also auch dasselbe Optimum — der Unterschied liegt in der Laufzeit, nicht in der Qualität. (c) ist falsch: CP-SAT rechnet ausschließlich mit ganzen Zahlen, kontinuierliche Zeiten kann es gerade nicht.
-
3 — (b). Mehrfache Optima sind bei Scheduling der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: assert plan == erwarteter_plan besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen assert makespan == 11 und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert num_workers und random_seed.
+
3 — (b). Mehrfache Optima sind bei Scheduling der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: assert plan == erwarteter_plan besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen assert makespan == 11 und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert num_workers und random_seed.
Selbsttest
Sie entfernt Werte aus den Wertebereichen, die aufgrund der Bedingungen unmöglich sind — bevor gesucht wird. Dadurch schrumpft der Suchbaum drastisch.
@@ -30628,7 +30628,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.8 Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“
8.2 — Unlösbarkeit. Die Summe aller Flusserhaltungsgleichungen ergibt \sum_i b_i = 0 (jede Kante taucht einmal mit +1 und einmal mit -1 auf). Ist die Summe ungleich null, widersprechen sich die Gleichungen. Bei Angebotsüberschuss führt man einen künstlichen Dummy-Senkenknoten mit dem Restbedarf und Kosten 0 ein.
@@ -30680,8 +30680,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Beachten Sie, dass Werk 3 seine günstigste Verbindung (L4 zu 5) voll ausschöpft und W2 ausschließlich L1 beliefert (9), obwohl L4 mit 7 billiger wäre — dort ist der Bedarf bereits von W3 gedeckt. (c) Die Lösung ist ganzzahlig, obwohl nicht gefordert — die Transportmatrix ist total unimodular, alle Ecken sind ganzzahlig.
8.4 — Zuordnung mit Verboten. Kosten auf einen sehr hohen Wert setzen (kosten[2][2] = 1e6) oder mit np.inf arbeiten (bei linear_sum_assignment erlaubt). Die Lösung weicht auf die zweitbeste Zuordnung für Carla aus; die Gesamtkosten steigen um die Differenz.
8.5 — Engpass finden. Die Dualwerte der Kapazitätsschranken (res.upper.marginals bei linprog) zeigen es direkt; alternativ jede Kapazität einzeln um 1 erhöhen und neu rechnen. Im Beispiel ist die Kante Werk_A → Umschlag der Engpass (voll ausgelastet, günstigster Weg).
-
8.6 — VRP variieren. (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald \text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf} (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) oder wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die Metaheuristik verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
-
8.7 — TSP mit MTZ. Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während OR-Tools weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
+
8.6 — VRP variieren. (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald \text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf} (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) oder wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die Metaheuristik verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
+
8.7 — TSP mit MTZ. Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während OR-Tools weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
Finde den Denkfehler — Die vergessene Dimension
Was die Fahrzeuge tun. Fahrzeuge 1, 2 und 3 fahren gar nicht — sie stehen mit null Stopps im Depot. Fahrzeug 4 bedient alle 16 Kunden und lädt dabei 37 Paletten bei einer Kapazität von 10. Es ist um 270 % überladen. Physikalisch ist dieser Plan nicht ausführbar; im Modell ist er die beste Lösung.
@@ -30730,7 +30730,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Es fehlt die Schranke.
Alles, was der Kollege sagt, stimmt. Der Fehler liegt darin, was er nicht sagt: Die Ersparnis wird gegen die bisherige Praxis gemessen, nicht gegen das Mögliche. Damit beantwortet die Zahl eine andere Frage als die, die das Management stellt.
Metaheuristik_vs_Exakt.py liefert die fehlende Zahl in der Spalte Schranke: 1 768 Minuten. Der Abstand der vorgestellten Lösung zum Bestmöglichen beträgt also bis zu 25 %, nicht 0 %. In derselben Nacht, in der 640 Stunden gefeiert werden, liegen möglicherweise weitere 2 000 Stunden ungenutzt herum.
-
Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist LNS.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
+
Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist LNS.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
Dasselbe Muster in anderer Verkleidung: Kapitel Handelsmaschine, Data_Snooping.py — dort fehlt nicht die Schranke, sondern die Zahl der Versuche. Beide Male macht eine weggelassene Kennzahl aus einem korrekten Ergebnis eine irreführende Aussage.
@@ -30790,14 +30790,14 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Der Fehler steckt in der Vorstellung, „aufrunden” koste höchstens eine Rolle. Aufgerundet wird nicht eine Zahl, sondern dreizehn — je Muster eine. Jede einzelne Aufrundung kostet bis zu eine Rolle, und in der Summe sind es sechs.
-
Die Schranke sagt: Weniger als 73 Rollen sind unmöglich. Sie sagt nicht: Jede zulässige Lösung liegt höchstens eine Rolle darüber. Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap in Abschnitt 6.8, nur in die andere Richtung.
+
Die Schranke sagt: Weniger als 73 Rollen sind unmöglich. Sie sagt nicht: Jede zulässige Lösung liegt höchstens eine Rolle darüber. Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap in Abschnitt 6.8, nur in die andere Richtung.
Was der Kollege stattdessen hätte tun müssen: das Master ein zweites Mal lösen, diesmal mit integrality=1 über genau die Spalten, die er ohnehin schon erzeugt hat. Das ist eine Zeile, dauert Millisekunden und liefert 73 statt 79. Er hatte alles dafür bereits vorliegen.
🎯 Die allgemeine Lehre Eine gebrochene LP-Lösung ist kein Plan, sondern eine Schranke. Der Weg zur ganzzahligen Lösung führt über den Solver, nicht über ceil().
Micro-Quiz
1. b) Das Mustermodell kennt keine einzelnen Rollen und damit keine austauschbaren Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund; (c) ist falsch, die LP-Lösung ist hier gerade nicht ganzzahlig (72,92).
-
2. b) Ein Rucksackproblem: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
+
2. b) Ein Rucksackproblem: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
3. b) Die untere Schranke von 33,60 beweist, dass mindestens 34 Rollen nötig sind. Damit weiß man, dass die 35 der Faustregel höchstens eine daneben liegen — und kann aufhören zu suchen. Genau diese Aussage kann eine Heuristik allein nie liefern (Kapitel 9).
Selbsttest
@@ -30810,8 +30810,8 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.11 Lösungen zu Kapitel „Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität“
11.1 — Konvexität einordnen. PSD (alle \ge 0) → konvex, aber nicht streng konvex; die Lösung ist nicht notwendig eindeutig. Der Eigenwert 0 bedeutet eine flache Richtung: Entlang des zugehörigen Eigenvektors ändert sich der quadratische Term nicht — es gibt eine Rinne statt eines Punktes.
-
11.2 — Komplementärer Schlupf. Ja, verträglich: \lambda_i w_i = 0 gilt für alle i (w_2 = 0 mit \lambda_2 > 0; die anderen mit \lambda = 0). \lambda_2 = 0{,}03 bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort bindend.
11.2 — Komplementärer Schlupf. Ja, verträglich: \lambda_i w_i = 0 gilt für alle i (w_2 = 0 mit \lambda_2 > 0; die anderen mit \lambda = 0). \lambda_2 = 0{,}03 bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort bindend.
11.4 — Unmögliche Korrelationsmatrix. Eigenwerte \approx (-0{,}62;\ 1{,}0;\ 2{,}62) → nicht PSD, also unmöglich. Anschaulich: Wenn A stark positiv mit B korreliert und B stark positiv mit C, kann A nicht gleichzeitig stark negativ mit C korrelieren — Korrelation ist eingeschränkt transitiv. Formal: \rho_{AC} \ge \rho_{AB}\rho_{BC} - \sqrt{(1-\rho_{AB}^2)(1-\rho_{BC}^2)} = 0{,}81 - 0{,}19 = 0{,}62; verlangt waren -0{,}9.
11.5 — Gewichtung untersuchen. Erwartetes Muster: Größeres \alpha → mehr Rendite, mehr Konzentration im Titel mit höchstem \mu. Größeres \beta → gleichmäßigere Gewichte, Entropie steigt Richtung \ln 4 = 1{,}386. Ab etwa \beta \approx 0{,}1 dominiert die Entropie und man nähert sich der Gleichgewichtung. Empfehlung an einen Ausschuss: Nicht mit \beta argumentieren, sondern mit der resultierenden Maximalposition — „mit dieser Einstellung liegt keine Position über 40 %“ ist verständlich, „\beta = 0{,}015“ nicht.
11.6 — Nicht-Konvexität demonstrieren. Mit der bewusst fehlerhaft konstruierten Matrix finden 20 Startpunkte typischerweise mehrere verschiedene Optima; die „Portfoliovarianz“ w^\top\Sigma w kann bei geeigneten Gewichten negativ werden (der zugehörige Eigenvektor liegt allerdings teilweise außerhalb des zulässigen Bereichs w \ge 0{,}001, \sum w = 1 — deshalb fällt der Fehler bei naiver Prüfung nicht auf).
@@ -30843,14 +30843,14 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
Prüfen, ob die Matrix positiv semidefinit ist (Zeile aus (b)) — beim Einlesen, nicht beim Optimieren.
Die Ursache suchen. Korrelationen aus verschiedenen Quellen, Zeiträumen oder Frequenzen sind fast nie widerspruchsfrei. Schätzen Sie die Matrix aus einem konsistenten Datensatz.
-
Ist das nicht möglich, die Matrix reparieren: negative Eigenwerte auf null setzen und rekonstruieren (Eigenwert-Clipping, siehe die Aufgabe Konvexität einer Kovarianzmatrix reparieren in Kapitel 2) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert (Kapitel 18).
+
Ist das nicht möglich, die Matrix reparieren: negative Eigenwerte auf null setzen und rekonstruieren (Eigenwert-Clipping, siehe die Aufgabe Konvexität einer Kovarianzmatrix reparieren in Kapitel 2) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert (Kapitel 18).
Der Merksatz dazu: Eine Kovarianzmatrix ist kein Behälter für einzeln geschätzte Zahlen, sondern ein geometrisches Objekt. Nicht jede Kombination von Korrelationen existiert.
Micro-Quiz
1 — (b). Der DCPError ist eine inhaltliche Aussage: Für diese Formulierung kann es keine Optimalitätsgarantie geben. Auf scipy auszuweichen (a) beseitigt die Meldung, nicht die Ursache — man bekommt dann ein lokales Ergebnis ohne Garantie und ohne Warnung, wie Abschnitt 11.8 zeigt. Toleranzen (c) haben mit Konvexität nichts zu tun. Richtig ist, zwischen zwei bewussten Wegen zu wählen: konvex umformulieren, oder lokal rechnen und das im Bericht kenntlich machen.
-
2 — (b) ein lokales Minimum.success: True beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von SLSQP eingehalten.
+
2 — (b) ein lokales Minimum.success: True beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von SLSQP eingehalten.
3 — (c) Schattenpreis.\lambda^* ist die Ableitung des optimalen Zielwerts nach der rechten Seite der Nebenbedingung — dieselbe Bedeutung wie der Dualwert im LP (Kapitel 5), nur für allgemeine, auch krumme Nebenbedingungen. (a) und (b) verwechseln den Multiplikator mit einer Verletzungszahl beziehungsweise mit dem Schlupf; der Schlupf ist bei einer bindenden Bedingung gerade null (komplementärer Schlupf).
Selbsttest
@@ -30863,12 +30863,12 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“
12.1 — Fluch des Durchschnitts. Weil sich Verzögerungen fortpflanzen und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (der nächste Schritt ist noch nicht bereit), ein verspätetes verzögert alles. Weitere Beispiele: Wartezeiten in Warteschlangen (nichtlinear in der Auslastung), Projektkosten mit Nachträgen.
-
12.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
+
12.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
12.3 — Zweistufig rechnen. (a) Mit Spot = 60: Ableitung bei x \in (100,250): 40 - 60\cdot0{,}5 + 5\cdot0{,}5 = 40-30+2{,}5 = +12{,}5 > 0 → Kapazität senken. Bei x < 100: 40 - 60 = -20 < 0 → erhöhen. Optimum daher x^* = 100. (b) Billige Nachbesserung macht Vorhalten unattraktiv. (c) Bei x = 225 optimal müsste die Ableitung dort das Vorzeichen wechseln — das ist bei diskreten Szenarien nur zufällig der Fall; der Mittelwert ist nur bei symmetrischen Kosten und stetiger Verteilung optimal.
-
12.4 — EVPI interpretieren. Selbst eine perfekte Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also niemals rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
+
12.4 — EVPI interpretieren. Selbst eine perfekte Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also niemals rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
12.5 — Monte-Carlo erweitern. (a) Servicelevel 95 %: Kapazität = 95 %-Quantil des Bedarfs (ca. 485). Die Zusatzkosten gegenüber dem Kostenoptimum (180) betragen ein Vielfaches — Servicelevel ist teuer, und genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (b) cvar = kosten[kosten >= np.quantile(kosten, 0.95)].mean(). (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben.
12.6 — Den Preis der Zusage selbst bestimmen. (a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die Norm, tragen zur gesicherten Leistung aber kaum bei. (b) Der Unterschied ist nicht numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert selbst der Vollausbau in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese Fälle in den Daten und meldet korrekt infeasible, sobald die Zusage über 92 % steigt. Das analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet optimal — und der Plan hält gemessen 87,4 %. Glauben sollte man dem Szenariomodell: Ein infeasible ist hier die richtige Antwort. Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. (c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“
\Gamma = 0 entspricht dem nominalen Fall, \Gamma = n dem vollen Worst Case. Dazwischen steuert \Gamma die Vorsicht stufenlos — der praktisch nützlichste Bereich liegt meist bei \Gamma \approx \sqrt{n}.
@@ -30880,7 +30880,7 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
\mathbb{E}\big[\max(D_1,\dots,D_5)\big] \;\ne\; \max\big(\mathbb{E}[D_1],\dots,\mathbb{E}[D_5]\big)
Anschaulich: Das Projekt ist fertig, wenn das letzte Gewerk fertig ist. Ein Gewerk, das statt 11 nur 7 Tage braucht, bringt niemandem etwas — die anderen sind ja noch dran. Ein Gewerk, das 17 Tage braucht, hält alle auf. Die guten Ausreißer verpuffen, die schlechten schlagen voll durch.
-
Damit gilt für das Maximum immer \mathbb{E}[\max] \ge \max(\mathbb{E}). Die Differenz ist kein Schätzfehler, sondern eine systematische Verzerrung — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
+
Damit gilt für das Maximum immer \mathbb{E}[\max] \ge \max(\mathbb{E}). Die Differenz ist kein Schätzfehler, sondern eine systematische Verzerrung — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
Warum 95,5 %. Damit das Projekt die geplanten 11,33 Tage hält, müssen alle fünf Gewerke gleichzeitig ihren Mittelwert unterbieten. Für die Dreiecksverteilung (6;\ 10;\ 18) ist die Wahrscheinlichkeit dafür je Gewerk
Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauert 17 Tage“, sondern: „In 9 von 10 Fällen sind wir nach 17 Tagen fertig; im Mittel nach 14,4.“ Diese zwei Zahlen sind eine belastbare Zusage — eine einzelne ist es nie.
Micro-Quiz
1 — (b). Das kritische Verhältnis 1400/(1400+120) = 0{,}921 gibt an, wie weit man sich auf die günstigere Fehlerseite stellen soll: Bestellt wird das 92,1-%-Quantil des Bedarfs, hier 38 Stück. (a) ignoriert die Kostenasymmetrie und kostet im Kapitelbeispiel 160 % mehr. (c) verwechselt die Fragestellung: Die Kapitalbindung ist bereits in den 120 € je überzähligem Stück enthalten — sie rechtfertigt keine zusätzliche Kürzung.
-
2 — (b). Der Unterschied liegt darin, was man wissen muss: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: Robuste Optimierung betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
+
2 — (b). Der Unterschied liegt darin, was man wissen muss: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: Robuste Optimierung betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
3 — (c). Bei durchgehender Linearität und ohne nachgelagerte Entscheidung gilt \mathbb{E}[f(X)] = f(\mathbb{E}[X]) — dann ist Rechnen mit Mittelwerten korrekt. Sobald aber ein Maximum, ein Minimum, ein Betrag oder eine Nachbesserungsentscheidung auftaucht, gilt das nicht mehr: Genau das sind die beiden Fälle aus Schnellstart (asymmetrische Kosten über maximum) und Abschnitt 12.9 (Maximum über parallele Vorgänge). (a) ist die Fehlannahme, um die es im ganzen Kapitel geht; (b) ist zu absolut — es gibt den linearen Fall, in dem es tatsächlich zulässig ist.
A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“
13.1 — Bausteine. Stufe = Tag 1–5; Zustand = verbleibende Distanz (ggf. plus Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung. Nicht in den Zustand gehören: bereits gelaufene Kilometer (redundant, wenn die Restdistanz bekannt ist) und das Wetter von gestern (ohne Einfluss auf die Zukunft).
-
13.2 — Optimalitätsprinzip. Weil der Wert eines Zustands nur von den künftigen Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von früheren Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit in den Zustand aufnehmen (dann wächst allerdings der Zustandsraum).
-
13.3 — Rückwärtsinduktion. Mit C(n) = n^2 + 2n und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad 2 \to 2 \to 1 (oder eine Permutation davon) mit Gesamtkosten 8 + 8 + 3 = 19. Alles auf einmal kostet 25 + 10 = 35.
-
13.4 — Rucksack als DP.
+
13.2 — Optimalitätsprinzip. Weil der Wert eines Zustands nur von den künftigen Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von früheren Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit in den Zustand aufnehmen (dann wächst allerdings der Zustandsraum).
+
13.3 — Rückwärtsinduktion. Mit C(n) = n^2 + 2n und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad 2 \to 2 \to 1 (oder eine Permutation davon) mit Gesamtkosten 8 + 8 + 3 = 19. Alles auf einmal kostet 25 + 10 = 35.
V = np.zeros((n +1, kapazitaet +1))for i inrange(1, n +1):for c inrange(kapazitaet +1):
@@ -30980,7 +30980,7 @@ F(11{,}33) = 1 - \frac{(18-11{,}33)^2}{(18-6)(18-10)} = 1 - \frac{44{,}4}{96} =
... kosten = (RUESTKOSTEN if (p >0andnot lief_vorher) else0) + ... rest, plan = V(t +1, neu, p >0)
-
Damit findet die Rückwärtsinduktion (3, 1, 4, 2) für 70 € — das nachgerechnete Optimum. Der Zustandsraum verdoppelt sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der Fluch der Dimensionalität.
+
Damit findet die Rückwärtsinduktion (3, 1, 4, 2) für 70 € — das nachgerechnete Optimum. Der Zustandsraum verdoppelt sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der Fluch der Dimensionalität.
Warum der richtige Betrag das Tückische ist. Man würde erwarten, dass ein falsches Modell auch einen falschen Kostenbetrag ausgibt — dann fiele es beim Nachrechnen auf. Hier nicht: Für den von ihm gewählten Plan (5,0,5,0) stimmt die Rechnung zufällig, weil in diesem Plan tatsächlich jede Produktionsperiode auf eine Pause folgt. Modellannahme und Wirklichkeit fallen für genau diese eine Lösung zusammen.
Begründung in einem Satz: „Für 646 Euro sparen wir 843 Kilogramm CO₂ — zu 77 Cent je Kilogramm, während wir intern mit 90 Cent rechnen.“
14.3 — Mehr Trassen. Mit 8 statt 5 Trassen wird der Engpass schwächer. Zu erwarten ist eine kürzere Front: Der Zielkonflikt entsteht ja allein aus der Knappheit; je mehr Trassen, desto näher rücken Kostenminimum und CO₂-Minimum zusammen. Im Grenzfall genügend vieler Trassen fallen beide zusammen — die Bahn ist billiger und sauberer, also gibt es gar keinen Konflikt mehr und die Front schrumpft auf einen Punkt.
Der Lehrpunkt: Ein Zielkonflikt ist keine Eigenschaft der Ziele, sondern der Knappheit. Wer ihn auflösen will, sollte zuerst prüfen, ob sich die Ressource vermehren lässt, statt über Gewichte zu verhandeln.
-
14.4 — Die Front der Relaxation. Ohne integrality ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also keinen Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
+
14.4 — Die Front der Relaxation. Ohne integrality ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also keinen Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
Genau das ist der Grund, warum das Problem in Lehrbüchern zur linearen Programmierung nicht vorkommt und in der betrieblichen Praxis ständig: Sobald „welcher Träger“, „welches Lager“ oder „welche Schicht“ entschieden wird, zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen die Einbuchtungen, in denen die nicht gestützten Lösungen liegen.
14.5 — Drei Ziele. Die Front wird zu einer Fläche im dreidimensionalen Zielraum. Das ε-Verfahren braucht dann ein Gitter über zwei Schranken statt einer Folge über eine — der Aufwand wächst von O(k) auf O(k^2), bei m Zielen auf O(k^{m-1}).
Zwei praktische Folgerungen: Erstens ist bei drei und mehr Zielen die vollständige Front meist nicht mehr bezahlbar; man arbeitet dann mit einer Stichprobe oder mit Metaheuristiken, die eine Front approximieren (NSGA-II, in pymoo enthalten). Zweitens ist eine Front mit hunderten Punkten für die Entscheidung ohnehin unbrauchbar — ab drei Zielen ist die bessere Frage meist, ob sich zwei davon zusammenfassen lassen.
Das ist die richtige Richtung und leicht zu merken: Je billiger der Überhang, desto großzügiger darf man ansetzen. Im Grenzfall kostenloser Entsorgung (c_+ \to 0) geht das kritische Verhältnis gegen 1 — man bestellt so viel, dass praktisch nie etwas fehlt.
15.2 — Wann ist der Mittelwert richtig? Nur wenn c_- = c_+und die Nachfrageverteilung symmetrisch ist. Dann liegt das kritische Verhältnis bei 0,5, das 50-%-Quantil ist der Median, und bei Symmetrie fällt der Median mit dem Mittelwert zusammen.
Beide Bedingungen sind unrealistisch. Fehlmenge und Überhang kosten fast nie dasselbe — bei Frischware ist der Überhang teurer, bei Ersatzteilen die Fehlmenge um Größenordnungen. Und Nachfrageverteilungen sind meist rechtsschief. Der Mittelwert ist damit die Ausnahme, nicht die Regel — er wird nur benutzt, weil Regressionsmodelle ihn standardmäßig liefern.
-
15.3 — Die Kennzahl der Prognoseabteilung. Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim MAPE, weil beide Maße den Erwartungswert belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
+
15.3 — Die Kennzahl der Prognoseabteilung. Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim MAPE, weil beide Maße den Erwartungswert belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
Das ist kein Zufall, sondern der Kern des Kapitels: Jedes rein statistische Fehlermaß ignoriert die Kostenasymmetrie. Man kann das Problem nicht lösen, indem man das Fehlermaß wechselt; man muss die Kosten selbst messen. (Die einzige Ausnahme ist der Pinball Loss — genau das Maß, das die Quantilregression minimiert, und das ist eben kein allgemeines Prognosemaß, sondern eines für ein bestimmtes Quantil.)
15.4 — Zwei Quantile. Das Band ist an Aktionstagen deutlich breiter — dort ist die Streuung im Modell fast viermal so groß. Genau das ist die Information, die eine Punktprognose plus fester Zuschlag wegwirft.
Ein praktischer Nebeneffekt: Ein solches Band ist die verständlichste Form, Unsicherheit an die Disposition zu berichten. „Zwischen 130 und 210” sagt einem Menschen mehr als „170 ± Sicherheitszuschlag”.
-
15.5 — Der Wert der Merkmale. Zu erwarten ist, dass die Kosten stärker steigen als der MSE. Der Grund: Das Merkmal „Aktion” trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) und die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
+
15.5 — Der Wert der Merkmale. Zu erwarten ist, dass die Kosten stärker steigen als der MSE. Der Grund: Das Merkmal „Aktion” trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) und die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
Der Lehrsatz dahinter: Der Wert eines Merkmals hängt davon ab, wofür man es benutzt. Eine Merkmalsauswahl, die nach MSE-Beitrag sortiert, wirft möglicherweise genau die Merkmale weg, die für die Entscheidung am wichtigsten sind — nämlich die, die etwas über die Unsicherheit sagen.
Finde den Denkfehler — „Wir haben die Prognose um 18 % verbessert“
Der Sicherheitszuschlag stammt aus den Residuen des alten Modells.
16.3 — Die Brücke rückwärts. Shrinkage hilft, wenn man eine Kovarianzmatrix aus wenigen Beobachtungen schätzt. Bei Lieferzeiten tritt genau dasselbe Problem auf, sobald man die Korrelationen zwischen Lieferanten braucht — und die braucht man, sobald Lieferanten gemeinsame Ursachen haben: derselbe Hafen, dasselbe Vorprodukt, dieselbe Region.
Bei 40 Lieferanten hat die Kovarianzmatrix 820 zu schätzende Einträge. Wer dafür 36 Monatswerte hat, schätzt 820 Zahlen aus 36 Beobachtungen — dasselbe Missverhältnis wie bei Aktienrenditen, mit denselben Folgen (Kovarianz_Falle.py).
Benötigt würden: Lieferzeit-Zeitreihen je Lieferant über denselben Zeitraum, gleich getaktet. Genau daran scheitert es in der Praxis meist — die Daten liegen in Bestellvorgängen, nicht in einer Matrix.
-
16.4 — CVaR mit Ganzzahligkeit. Man ergänzt Binärvariablen y_j je Lieferant und die Kopplung w_j \le y_j sowie \sum_j y_j \le 3. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt cp.Variable(n, boolean=True).
+
16.4 — CVaR mit Ganzzahligkeit. Man ergänzt Binärvariablen y_j je Lieferant und die Kopplung w_j \le y_j sowie \sum_j y_j \le 3. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt cp.Variable(n, boolean=True).
Zu erwarten ist ein höherer CVaR: Der zulässige Bereich wird kleiner, und die Feinabstimmung über sechs Lieferanten fällt weg. Der Aufschlag ist der Preis der Vertragswirklichkeit — und genau die Zahl, die man dem Einkauf vorlegt, wenn dort jemand sagt, drei Lieferanten seien genug.
16.5 — Die eigene Brücke. Freie Antwort. Eine gute Bearbeitung nennt das Modell, die Entsprechung und die Zeile der Grenztabelle, die im Weg steht — meist ist es die dritte (Teilbarkeit) oder die erste (gemessen gegen geschätzt). Wenn keine im Weg steht, ist die Übertragung wahrscheinlich zu oberflächlich geprüft.
Finde den Denkfehler — „Das ist doch dasselbe Problem“
Ein LP sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
+
Ein LP sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
„Stellen Sie sich vor, Ihre Engpassmaschine wäre nicht die Fräse, sondern eine Vorschrift: Sie dürfen nur eine bestimmte Menge Risiko in den Büchern haben. Der Schattenpreis sagt dann, was eine Lockerung dieser Vorschrift wert wäre — genau wie bei einer zusätzlichen Maschinenstunde.”
Fette Ränder (seltene, aber sehr große Abweichungen). Die Standardabweichung mittelt sie weg und bestraft außerdem Abweichungen nach oben genauso wie nach unten; CVaR sieht ausschließlich auf den schlechten Rand.
Log-Renditen sind Differenzen von Logarithmen — die addieren sich über die Zeit. Diskrete Renditen sind lineare Anteile am Kapital — die addieren sich über gewichtete Positionen.
Die Optimierung sucht die Richtungen kleinster geschätzter Varianz — genau jene, deren Eigenwerte am stärksten nach unten verzerrt sind. Sie optimiert dadurch in das Schätzrauschen hinein.
-
Der Rang von \mathbf{X}^\top\mathbf{X} ist höchstens T < N — die Matrix ist nicht invertierbar, das GMV-Problem hat unendlich viele Lösungen.
+
Der Rang von \mathbf{X}^\top\mathbf{X} ist höchstens T < N — die Matrix ist nicht invertierbar, das GMV-Problem hat unendlich viele Lösungen.
Stichprobenmatrix (unverzerrt, verrauscht) mit strukturiertem Ziel (verzerrt, stabil). \delta wird analytisch so bestimmt, dass der erwartete quadratische Fehler minimal wird.
Skalierte Einheitsmatrix (sklearn) und Konstant-Korrelations-Ziel (LW 2003).
Beste Sharpe Ratio unter den vier Kandidaten: (0{,}3;\ 0{,}7). Das exakte Optimum liegt bei w_1 = 0{,}318. Bemerkenswert: Die Mischung (0{,}3;\ 0{,}7) hat mit 10,08 % eine geringere Volatilität als beide Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
+
Beste Sharpe Ratio unter den vier Kandidaten: (0{,}3;\ 0{,}7). Das exakte Optimum liegt bei w_1 = 0{,}318. Bemerkenswert: Die Mischung (0{,}3;\ 0{,}7) hat mit 10,08 % eine geringereVolatilität als beide Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
19.2 — Lambda deuten. Von \lambda=0 (GMV, linker unterer Punkt der Kurve) wandert die Lösung entlang der Effizienzgrenze nach rechts oben, bis sie bei \lambda\to\infty im Titel mit der höchsten Rendite endet (bzw. an der Positionsobergrenze).
-
19.3 — Korn-Transformation.\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}} — bei einem Portfolio mit \sum w_i = 1 und Überrenditen geschrieben als \mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1}) kürzt sich c heraus. Mit der Bedingung \sum w_i = 1 ist die Skala jedoch fixiert, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch \sum y_i = \kappa mit freiem \kappa und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
+
19.3 — Korn-Transformation.\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}} — bei einem Portfolio mit \sum w_i = 1 und Überrenditen geschrieben als \mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1}) kürzt sich c heraus. Mit der Bedingung \sum w_i = 1 ist die Skala jedoch fixiert, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch \sum y_i = \kappa mit freiem \kappa und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
19.4 — Restriktionen kosten. Erwartetes Muster: Sharpe Ratio sinkt monoton mit strengerer Grenze. Unlösbar wird es bei w_{\max} < 1/n — dann kann die Summe der Gewichte 1 nicht mehr erreicht werden.
19.5 — Den Sektorfehler nachstellen. (a) AAPL, AMZN, CVX, GS statt AAPL, MSFT, NVDA, AMZN. (b) Die Gewichte unterscheiden sich deutlich, weil die eigentlich zu begrenzenden Tech-Titel frei laufen. (c) Nein — die Kennzahlen sehen völlig plausibel aus. Genau das macht den Fehler so gefährlich.
19.6 — Kardinalität. Erwartung: Die Sharpe Ratio sinkt leicht, die Rechenzeit steigt deutlich (MIQP statt QP). Bei K \ge 5 und w_{\max} = 0{,}20 ist die Restriktion praktisch nicht mehr bindend.
Und die Kontrollfrage für den Alltag: Rechnen Sie Ihr Modell mit den Daten des halben Zeitraums und dann mit denen der anderen Hälfte. Wenn die Gewichte dabei stark springen, optimieren Sie Rauschen — unabhängig davon, wie gut die Kennzahlen im Schätzzeitraum aussehen.
Micro-Quiz
-
1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
+
1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
2 — (b) es braucht keine Renditeschätzung. Renditen sind die mit Abstand unzuverlässigste Eingangsgröße (Standardfehler größer als der geschätzte Wert selbst); wer sie nicht benötigt, umgeht das Problem vollständig. (a) trifft nicht zu — die Nebenbedingungen sind dieselben. (c) ist falsch: Das Minimum-Varianz-Portfolio erzielt erwartungsgemäß weniger Rendite; sein Vorteil liegt in der Verlässlichkeit, nicht in der Höhe.
3 — (b) Schätzfehler. Eine Konzentration von 44 % auf einen von zwölf Titeln ist das typische Bild eines Optimierers, der einem Rauschsignal folgt — im Kapitelbeispiel entsteht sie sogar dann, wenn alle Anlagen nachweislich identisch sind. (a) mag zutreffen, ist aber die unwahrscheinlichere Erklärung und muss belegt werden, nicht angenommen. (c) beschriebe ein anderes Fehlerbild: Eine falsch skalierte Kovarianzmatrix führt zu unplausiblen Risikowerten, nicht zu Konzentration.
A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“
-
20.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \text{VaR}_{80\%} = 2{,}1. $_{80%} = $ Mittel der schlechtesten 20 % = (5{,}4+8{,}9)/2 = \mathbf{7{,}15}.
-
20.2 — Subadditivität. Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
+
20.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \text{VaR}_{80\%} = 2{,}1. $_{80%} = $ Mittel der schlechtesten 20 % = (5{,}4+8{,}9)/2 = \mathbf{7{,}15}.
+
20.2 — Subadditivität. Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
20.3 — Rockafellar-Uryasev nachvollziehen. (a) Schlechtestes Drittel von (1,4,9) ist \{9\} → CVaR = 9. (b) Mit \frac{1}{S(1-\alpha)} = \frac{1}{3\cdot(1/3)} = 1: \gamma=0: 0 + (1+4+9) = 14. \gamma=1: 1 + (0+3+8) = 12. \gamma=4: 4 + (0+0+5) = \mathbf{9}. \gamma=5: 5+4 = 9. \gamma=9: 9+0 = 9. (c) Minimum ab \gamma = 4 bei 9 — identisch mit (a) ✓. (Das Minimum wird auf einem ganzen Intervall angenommen, weil die Funktion stückweise linear ist — genau der Grund, warum der CVaR nicht streng konvex ist.)
20.4 — Einheiten prüfen. In einem Modell, das annualisierte Rendite gegen täglichen CVaR verrechnet, wirkt 1{,}5 effektiv als 1{,}5/252 \approx 0{,}006 auf Tagesbasis — der Risikoterm ist also um Faktor 252 zu leicht gewichtet. Um dieselbe Wirkung wie RISIKOAVERSION = 1.5 im konsistenten Tagesmodell zu erzielen, hätte man dort lambda_risk = 1.5 * 252 = 378 setzen müssen.
20.5 — Risikoaversion kalibrieren. (a) Konkav, ähnlich der Markowitz-Frontier, aber im Rendite-CVaR-Raum. (b) Bei Normalverteilung entspricht CVaR-Optimierung ungefähr der Varianz-Optimierung; die Ergebnisse divergieren umso stärker, je schiefer die Verteilung ist. (c) Empfehlung ohne Fachjargon: „Bei dieser Einstellung liegt der durchschnittliche Verlust an den schlechtesten fünf Prozent der Tage bei X Prozent — bei einer erwarteten Rendite von Y Prozent.“ Das ist entscheidbar; „\lambda = 4“ ist es nicht.
Warum der CVaR das nicht kann. Der CVaR mittelt über den gesamten Schwanz, statt an seiner Grenze stehenzubleiben. Damit sieht er den Ausfall in jedem der drei Fälle, und das Ergebnis verhält sich wie erwartet: 79,59 € und 79,66 € einzeln, 101,33 € zusammen statt 159,24 € — die Diversifikation senkt das Risiko um rund ein Drittel.
Formal folgt die Subadditivität daraus, dass der CVaR sich als Maximum über Erwartungswerte schreiben lässt (Darstellungssatz für kohärente Risikomaße), und Maxima von Erwartungswerten sind stets subadditiv. Anschaulicher: Ein Mittelwert über eine Menge verhält sich gutartig, wenn man Mengen zusammenlegt; ein Quantil nicht — es kann springen, sobald sich die Reihenfolge der Szenarien ändert.
-
Die praktische Konsequenz ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den Expected Shortfall — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
+
Die praktische Konsequenz ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den Expected Shortfall — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
Micro-Quiz
1 — (b) nichts. Der VaR ist ein Quantil: Er markiert die Schwelle und sagt nichts über den Bereich dahinter. Genau das zeigt der Schnellstart dieses Kapitels — zwei Anlagen mit identischem VaR von 3,00 %, aber CVaR 3,00 % gegen 9,17 %. (a) verwechselt die Schwelle mit dem, was hinter ihr liegt. (c) ist ebenfalls falsch: Anlage B hat zwar die höhere Schwankung, aber die Schwankung allein sagt nichts über die Form des Schwanzes — genau deshalb reicht auch die Varianz als Risikomaß nicht aus.
2 — (b). Rockafellar und Uryasev zeigen, dass sich der CVaR als Minimum über eine Hilfsvariable \gamma schreiben lässt; mit Schlupfvariablen für die Terme \max(\cdot, 0) wird daraus ein gewöhnliches LP. Die VaR-Minimierung ist dagegen nicht-konvex (das Quantil springt) und hat viele lokale Optima — dasselbe Problem wie in Abschnitt 11.6. (a) ist zwar zutreffend, aber nicht der entscheidende Grund; (c) ist eine wahre Aussage ohne Bezug zur Optimierbarkeit.
A.21 Lösungen zu Kapitel „Die vollständige quantitative Handelsmaschine“
-
21.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) Survivorship-Bias — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
+
21.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) Survivorship-Bias — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
21.2 — Kennzahlen deuten. A: Sharpe = (12-2)/22 = 0{,}45, Calmar = 12/35 = 0{,}34. B: Sharpe = (8-2)/9 = 0{,}67, Calmar = 8/12 = 0{,}67. Empfehlung: B — für einen Pensionsfonds ist der Drawdown entscheidend, weil laufende Auszahlungen in einer Verlustphase Substanz vernichten. B ist in beiden risikoadjustierten Maßen besser.
-
21.3 — Rebalancing-Kalender. Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
+
21.3 — Rebalancing-Kalender. Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
21.4 — Rebalancing-Frequenz. Typisches Muster: Turnover und Kosten steigen etwa linear mit der Frequenz, der Bruttoertrag verbessert sich nur unterproportional. Bei 0,15 % Gebühren ist monatlich meist vertretbar, bei 0,5 % eher quartalsweise. Ein Toleranzband (nur handeln bei Abweichung > x %) schlägt fast immer die feste Frequenz.
21.5 — Krisenverhalten. Erwartung: Die Überrendite ist selten stabil; oft stammt sie aus wenigen Perioden. Folgerung: Eine gute Gesamtkennzahl kann von einer einzigen glücklichen Phase getragen sein — immer nach Teilzeiträumen aufschlüsseln.
-
21.6 — Data Snooping messen. Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die Differenz zwischen (a) und (c) ist der Selektionseffekt — genau das, was die Deflated Sharpe Ratio korrigieren soll.
+
21.6 — Data Snooping messen. Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die Differenz zwischen (a) und (c) ist der Selektionseffekt — genau das, was die Deflated Sharpe Ratio korrigieren soll.
Finde den Denkfehler — Die Strategie mit dem hochsignifikanten Ergebnis
-
Warum ein korrekter Backtest wertlos sein kann. Der Backtest misst genau das, was er messen soll: die Wertentwicklung dieser einen Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: „Ist dieses Ergebnis besser, als es Zufall erklären kann?“
+
Warum ein korrekter Backtest wertlos sein kann. Der Backtest misst genau das, was er messen soll: die Wertentwicklung dieser einen Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: „Ist dieses Ergebnis besser, als es Zufall erklären kann?“
Und für diese Frage ist entscheidend, dass die Strategie als Beste aus hundert ausgewählt wurde. Ein p-Wert von 0,0094 bedeutet: „Wenn diese Strategie keinen Vorteil hätte, sähe sie in 0,94 % der Fälle so gut aus.“ Bei hundert Versuchen erwartet man aber rund fünf Ergebnisse unter dem 5-%-Niveau — allein durch Zufall. Genau das zeigt die Tabelle: Auf reinem Rauschen liefert die Beste von hundert im Mittel Sharpe 1,13 und p = 0,0094. Dieselben Zahlen.
Der Fehler steckt nicht im Backtest, sondern in der Auswahl. Deshalb ist er auch durch noch so sorgfältiges Programmieren nicht zu verhindern.
1 — (b) das Zeitlimit greift. Eine Laufzeit, die exakt dem Limit entspricht und über Monate konstant bleibt, ist kein Stabilitätsbeleg, sondern zeigt, dass der Solver jedes Mal abgebrochen wird. (a) ist die Fehldeutung, um die es im Denkfehler geht. (c) läge nahe, wenn die Laufzeit schwankte oder das Limit überschritte — hier ist die Ursache aber im Modell und in der Datenmenge zu suchen, nicht in der Hardware.
2 — (b) aus dem ausgegebenen Tourenplan. Eine Prüfung muss von der Lösung ausgehen und die Anforderungen unabhängig nachrechnen. (a) fragt die Ladungsdimension — also ausgerechnet den Baustein, dessen Fehlen der Fehler war (siehe Abschnitt 8.7); existiert sie nicht, stürzt die Prüfung ab, existiert sie, kann sie per Konstruktion nie verletzt sein. (c) prüft gar nichts: Ob eine Kapazität in der Zielfunktion bepreist ist, sagt nichts darüber, ob sie eingehalten wurde.
-
3 — (c) hierarchisch lockern.INFEASIBLE sagt nur, dass ein Widerspruch existiert, nicht welche Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der Kostenzerlegung. Genau das tut Infeasibility_Diagnose.py. (a) hilft nicht: INFEASIBLE ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
+
3 — (c) hierarchisch lockern.INFEASIBLE sagt nur, dass ein Widerspruch existiert, nicht welche Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der Kostenzerlegung. Genau das tut Infeasibility_Diagnose.py. (a) hilft nicht: INFEASIBLE ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
Selbsttest
Durch hierarchische Relaxation: Schlupfvariablen mit hohen, gestaffelten Strafkosten für die verletzbaren Bedingungen.
Die erste wird getötet (test_pruefung_findet_kapazitaetsverletzung). Die zweite ist interessanter: Sie macht die Prüfung strenger statt schwächer — eine Lösung mit einer Menge von exakt 0 würde als negativ beanstandet. Ob sie überlebt, hängt daran, ob die Suite eine Instanz enthält, in der ein Produkt mit Menge 0 vorkommt. In der Schreinerei ist das nicht der Fall — die Mutation überlebt also und zeigt eine echte Lücke: Es fehlt eine Testinstanz, in der ein Produkt nicht produziert wird.
23.4 — Der Benchmark mit MILP. Zu erwarten ist, dass sich die Reihenfolge ändert. Beim reinen LP entscheidet vor allem der Modellaufbau in Python; beim MILP verschiebt sich das Gewicht zum Lösen, und dort spielen die Branch-and-Bound-Heuristiken der Bibliotheken gegeneinander. Die Spalte „Anteil” sollte bei allen deutlich fallen — nicht weil der Aufbau schneller würde, sondern weil das Lösen langsamer wird. Genau deshalb steht im Kapitel, dass die Tabelle nichts über MILPs sagt.
-
23.5 — Der Dienst mit Zeitlimit. Der Modellbauer in or_kern.py nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei GLOP solver.SetTimeLimit(millisekunden)). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver ZEITLIMIT, ist stand weiterhin gescheitert, aber mit einer anderen Begründung als bei UNZULAESSIG — der Unterschied zwischen „rechne länger” und „ändere das Modell” (Kapitel 6).
+
23.5 — Der Dienst mit Zeitlimit. Der Modellbauer in or_kern.py nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei GLOPsolver.SetTimeLimit(millisekunden)). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver ZEITLIMIT, ist stand weiterhin gescheitert, aber mit einer anderen Begründung als bei UNZULAESSIG — der Unterschied zwischen „rechne länger” und „ändere das Modell” (Kapitel 6).
Für den Test braucht es eine Instanz, die das Limit reißt. Ein LP eignet sich schlecht dafür; nehmen Sie ein MILP mit einigen hundert Binärvariablen und ein Limit von 0,1 Sekunden.
Finde den Denkfehler — „Die Suite ist grün, das Modell stimmt”
„Grün” heißt nur, dass kein Test fehlgeschlagen ist — das gilt auch für eine Suite aus lauter assert True. „Prüft etwas” heißt, dass die Tests bei einem eingebauten Fehler rot würden; genau das misst der Mutationstest.
Weil sonst nicht erkennbar ist, welche Hälfte die Zeit kostet. Im Kapitel gehen bei OR-Tools 78 % in den Aufbau — wer nur die Gesamtzeit sieht, wechselt den Solver und ändert damit fast nichts.
Weil die Rechnung Sekunden bis Minuten dauert. Eine synchrone Antwort läuft in den Timeout des Reverse Proxy und blockiert währenddessen einen Arbeiter. 202 heißt „angenommen, noch nicht fertig” — genau die richtige Aussage.
-
Ein Threadpool genügt, wenn der Solver den GIL freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während Solve(), im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
+
Ein Threadpool genügt, wenn der Solver den GIL freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während Solve(), im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
⚠️ Die häufigste Verwechslung:L\,y_t \le x_tje Periode (B2) ist ein anderes Modell — es verlangt in jeder einzelnen Periode eine Mindestmenge und ist erheblich strenger. Wer den Jahresvertrag so formuliert, erzeugt ein unlösbares Modell und sucht den Fehler dann in den Daten.
-
⚠️ Die obere Kopplung nicht vergessen. Ohne \sum_t x_t \le U\,y kann das Modell y = 0 setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der Trickle Flow aus Abschnitt 6.5, nur andersherum. U ist die Jahreskapazität des Lieferanten, keine runde Zahl.
+
⚠️ Die obere Kopplung nicht vergessen. Ohne \sum_t x_t \le U\,y kann das Modell y = 0 setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der Trickle Flow aus Abschnitt 6.5, nur andersherum. U ist die Jahreskapazität des Lieferanten, keine runde Zahl.
B27 — Budgetlimit
Regel. „Alle Maßnahmen zusammen dürfen 2 Mio. € nicht überschreiten.“
\sum_i c_i x_i \le B
-
Mit x_i \in \{0,1\} ist das ein Rucksackproblem — dasselbe Muster, das Kapitel 6 an Rucksack.py vorrechnet und Kapitel 10 als Pricing-Teilproblem wiederverwendet.
+
Mit x_i \in \{0,1\} ist das ein Rucksackproblem — dasselbe Muster, das Kapitel 6 an Rucksack.py vorrechnet und Kapitel 10 als Pricing-Teilproblem wiederverwendet.
modell.Add(sum(KOSTEN[i] * x[i] for i in massnahmen) <= BUDGET)# Mehrere Toepfe: je Topf eine Zeile - NICHT die Summe ueber alle Toepfefor topf, grenze in BUDGETS.items(): modell.Add(sum(KOSTEN[i] * x[i] for i in massnahmenif TOPF[i] == topf) <= grenze)
-
💡 Der Schattenpreis des Budgets ist die Zahl, nach der die Geschäftsführung fragt: „Was bringt der nächste Euro?“ Bei einem LP ist er direkt ablesbar. Bei Ganzzahligkeit gibt es ihn nicht — die Dualwerte der Relaxation sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (Abschnitt 5.9).
+
💡 Der Schattenpreis des Budgets ist die Zahl, nach der die Geschäftsführung fragt: „Was bringt der nächste Euro?“ Bei einem LP ist er direkt ablesbar. Bei Ganzzahligkeit gibt es ihn nicht — die Dualwerte der Relaxation sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (Abschnitt 5.9).
⚠️ Die LP-Relaxation verspricht zu viel. Sie darf die letzte Maßnahme anteilig kaufen und liefert deshalb eine Schranke, die spürbar über dem tatsächlich Erreichbaren liegen kann. Wer sie als Prognose berichtet, verspricht Geld, das nicht kommt.
Die Strafe muss hoch genug sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber endlich, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
+
Die Strafe muss hoch genug sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber endlich, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
B19 — Hierarchische Ziele (lexikografisch)
Regel. „Erst die Besetzung sicherstellen, dann die Fairness optimieren.“
Variante A — Gewichtung: Strafen um Größenordnungen staffeln (10\,000 \gg 100 \gg 1). Einfach, aber bei extremen Skalenunterschieden numerisch heikel.
Ohne Symmetriebrechung durchsucht Branch-and-Bound k! gleichwertige Lösungen. Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.
+
Ohne Symmetriebrechung durchsucht Branch-and-Boundk! gleichwertige Lösungen. Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.
B21 — Worst-Case-Abzug (robuste Formulierung)
Regel. „Rechne nicht mit dem geschätzten Wert, sondern mit dem ungünstigsten plausiblen.“
Bei Box-Unsicherheit \mu_i \in [\hat\mu_i - \delta_i, \hat\mu_i + \delta_i] und w \ge 0: \hat{\boldsymbol{\mu}}^\top\mathbf{w} \;\longrightarrow\; \hat{\boldsymbol{\mu}}^\top\mathbf{w} - \boldsymbol{\delta}^\top\mathbf{w}
💡 Ganzzahligkeit nicht fordern! Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist scipy.optimize.linear_sum_assignment (Ungarischer Algorithmus, O(n^3)) noch deutlich schneller.
+
💡 Ganzzahligkeit nicht fordern! Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist scipy.optimize.linear_sum_assignment (Ungarischer Algorithmus, O(n^3)) noch deutlich schneller.
B24 — Subtour-Eliminierung
MTZ (einfach, aber schwach):u_i - u_j + C x_{ij} \le C - d_j \qquad \forall i\ne j
-
Besser in der Praxis:AddCircuit in CP-SAT oder die Routing-Bibliothek von OR-Tools.
Nach dem Lösen zeigen die Schlupfvariablen mit Wert > 0 präzise, welche Bedingung wo und um wie viel verletzt werden musste. Das ist zugleich die produktionstaugliche Lösung (Muster B18).
-
Schritt 4 — Zeitfenster und Erreichbarkeit prüfen. Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?
+
Schritt 4 — Zeitfenster und Erreichbarkeit prüfen. Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?
for kunde, (frueh, spaet) inenumerate(zeitfenster):assert distanz[depot][kunde] <= spaet, f"Kunde {kunde} nicht rechtzeitig erreichbar"
-
Schritt 5 — Rundungs- und Einheitenfehler. Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: sum(w) == 1 mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
+
Schritt 5 — Rundungs- und Einheitenfehler. Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: sum(w) == 1 mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen
Warum Schritt 2 so oft ins Leere läuft. Bedingungen einzeln abzuschalten funktioniert nur, solange es einen Schuldigen gibt. Überlagern sich zwei unabhängige Widersprüche, bleibt das Modell nach jeder einzelnen Abschaltung unlösbar — die Suche endet mit null Treffern, obwohl beide Widersprüche unverändert im Modell stehen. Genau dann steht man vor einem Modell mit tausenden Restriktionen und einem Statuscode.
Der Deletion Filter dreht die Frage um. Er fragt nicht „ist diese Bedingung schuld?“, sondern „wird diese Bedingung für den Widerspruch überhaupt gebraucht?“ — und das ist eine Frage, die sich beantworten lässt:
Nimm eine Bedingung versuchsweise heraus. Bleibt der Rest unlösbar, wurde sie nicht gebraucht: weg damit, endgültig. Wird der Rest lösbar, war sie beteiligt: sie bleibt.
-
Nach genau einem Durchlauf über alle n Bedingungen ist die übrig gebliebene Menge unreduzierbar — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein Irreducible Infeasible Subset, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion an (computeIIS bei Gurobi, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet n Aufrufe des Solvers statt 2^n durchprobierter Teilmengen.
+
Nach genau einem Durchlauf über alle n Bedingungen ist die übrig gebliebene Menge unreduzierbar — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein Irreducible Infeasible Subset, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion an (computeIIS bei Gurobi, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet n Aufrufe des Solvers statt 2^n durchprobierter Teilmengen.
#!/usr/bin/env python3# Konfliktsuche.py
@@ -32363,7 +32363,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
====================================================================
Vier Dinge sind daran wichtig.
-
Die Zulässigkeitsprüfung braucht keine Zielfunktion.linprog bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
+
Die Zulässigkeitsprüfung braucht keine Zielfunktion.linprog bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
Gefiltert werden nur die aufgeführten Bedingungen, nicht die Variablenschranken. Die Nichtnegativität steht in bounds und bleibt in jedem Teilmodell stehen. Sitzt Ihr Widerspruch in den Schranken (x >= 5 als Bound statt als Zeile), findet ihn der Filter nicht — schreiben Sie solche Grenzen dann als benannte Bedingung.
Sprechende Namen sind kein Luxus. Der Befund ist genau so brauchbar wie die Namen, die darin vorkommen. constraint_47, constraint_112, constraint_9 ist kein Befund.
Der Filter liefert einen kleinsten Konflikt, nicht den kleinsten. Dieses Modell mit vierzehn Bedingungen enthält neun verschiedene minimale Konflikte; welchen man zu sehen bekommt, entscheidet allein die Prüfreihenfolge. Das ist kein Mangel des Verfahrens, sondern eine Eigenschaft des Problems.
@@ -32372,7 +32372,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
⚠️ Was der Filter kostet Ein Konflikt kostet n Solverläufe. Bei 14 Bedingungen sind das 14 — bei 50 000 Restriktionen mit je zehn Sekunden Lösungszeit wären es knapp sechs Tage. Für große Modelle filtert man deshalb nicht einzeln, sondern gruppenweise: erst über Bedingungsblöcke (alle Kapazitäten, alle Verträge, alle Zeitfenster) laufen, dann den Filter nur noch innerhalb des einen Blocks anwenden, der übrig bleibt. Das ist derselbe Algorithmus auf einer gröberen Ebene und senkt die Zahl der Läufe um Größenordnungen.
-
Das Verhältnis zur Relaxation. Der Deletion Filter beantwortet die Frage „was widerspricht sich?“; die Schlupfvariablen aus Schritt 3 beantworten die Frage „was tun wir jetzt?“. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (Infeasibility_Diagnose.py, Abschnitt 22.3). Ein System, das im Ernstfall den Konflikt benennt und trotzdem einen Notfallplan liefert, hat beides.
+
Das Verhältnis zur Relaxation. Der Deletion Filter beantwortet die Frage „was widerspricht sich?“; die Schlupfvariablen aus Schritt 3 beantworten die Frage „was tun wir jetzt?“. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (Infeasibility_Diagnose.py, Abschnitt 22.3). Ein System, das im Ernstfall den Konflikt benennt und trotzdem einen Notfallplan liefert, hat beides.
🎯 Die eigentliche LehreINFEASIBLE in Produktion ist ein Entwurfsfehler, kein Betriebsfehler. Ein System, das im Ernstfall nur „geht nicht“ sagt, ist wertlos. Bauen Sie Schlupfvariablen von vornherein ein (Abschnitt 22.3).
@@ -32400,9 +32400,9 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
loesen(); t2 = time.perf_counter()print(f"Aufbau {t1-t0:.2f}s | Loesen {t2-t1:.2f}s")
Bei CVXPY und bei Schleifen über Szenarien ist oft der Aufbau der Engpass, nicht der Solver (siehe Abschnitt 20.6).
-
2. Big-M zu groß? Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie M auf die kleinste gültige Schranke (Abschnitt 6.5).
+
2. Big-M zu groß? Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie M auf die kleinste gültige Schranke (Abschnitt 6.5).
3. Symmetrie im Modell? Identische Maschinen, austauschbare Mitarbeitende, gleichwertige Fahrzeuge — der Solver durchsucht alle Vertauschungen. Abhilfe: Ordnungsbedingungen (Muster B20).
-
4. Falsche Solverfamilie? Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit scipy.optimize.minimize — jeweils Größenordnungen langsamer als das passende Werkzeug (Abschnitt 3.6).
+
4. Falsche Solverfamilie? Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit scipy.optimize.minimize — jeweils Größenordnungen langsamer als das passende Werkzeug (Abschnitt 3.6).
5. Wird vektorisiert?
# langsam: # schnell:for s inrange(S): constraints.append(u >=-(R @ w) - gamma)
@@ -32449,9 +32449,9 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
@@ -32474,12 +32474,12 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
print(f"Kondition {eig.max()/eig.min():.0f} | kleinster EW {eig.min():.2e}")# Faustregel: Kondition > 1000 ist bedenklich, > 10000 kritisch
Derselbe Fehler außerhalb der Finanzwelt
-
Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^7) und Tonnen-Angaben (10^{-3}) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
+
Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^7) und Tonnen-Angaben (10^{-3}) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")# Grobe Peilung: Wertebereiche der Koeffizienten anschauenprint(f"kleinster / groesster Betrag: {np.abs(A[A !=0]).min():.1e} "f"/ {np.abs(A).max():.1e}")
-
Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. Skalierung_Kondition.py zeigt beides samt Ruiz-Equilibrierung (Abschnitt 2.7).
+
Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. Skalierung_Kondition.py zeigt beides samt Ruiz-Equilibrierung (Abschnitt 2.7).
C6 — Falsche Dualwerte
Symptom. Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource.
@@ -32487,16 +32487,16 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
1. Vorzeichenkonvention. Haben Sie zur Maximierung negiert?
schattenpreise =-res.ineqlin.marginals # bei linprog nach Negation
Genau das ist die Vorzeichenfalle aus Abschnitt 5.7.
Dieser Test ist unabhängig von jeder Vorzeichenkonvention. Nutzen Sie ihn.
-
4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung. Die drei Prüfungen oben setzen voraus, dass es einen richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern mehrdeutig: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
+
4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung. Die drei Prüfungen oben setzen voraus, dass es einen richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern mehrdeutig: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
aktiv =int(np.sum(res.slack <1e-9)) # wie viele Bedingungen sind straff?if aktiv >len(res.x):print(f"Entartet: {aktiv} aktive Bedingungen bei {len(res.x)} Variablen.")
@@ -32509,7 +32509,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
C8 — DCPError
cvxpy.error.DCPError: Problem does not follow DCP rules.
-
Bedeutung. CVXPY kann die Konvexität Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. Das ist ein Feature.
+
Bedeutung. CVXPY kann die Konvexität Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. Das ist ein Feature.
@@ -32527,7 +32527,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Ursache.ortools und highspy bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen nicht im selben Prozess importieren (siehe Abschnitt 3.5). Der Konflikt entsteht auch indirekt: cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit — ein Skript, das erst cvxpy und dann ortools importiert, crasht daher mit derselben Meldung.
+
Ursache.ortools und highspy bringen beide eine eigene HiGHS-Kopie mit; sie lassen sich auf vielen Systemen nicht im selben Prozess importieren (siehe Abschnitt 3.5). Der Konflikt entsteht auch indirekt: cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit — ein Skript, das erst cvxpy und dann ortools importiert, crasht daher mit derselben Meldung.
Abhilfen (in dieser Reihenfolge): 1. Nur eines von beiden im selben Skript verwenden. 2. Getrennte Prozesse — ein ProcessPoolExecutor mit mp_context="spawn" und max_tasks_per_child=1, siehe Ein_System_Vier_Ansaetze.py. 3. Auf highspy verzichten: HiGHS ist ohnehin Backend von scipy.optimize.linprog und CVXPY. 4. Getrennte virtuelle Umgebungen.
C10 — Verdächtig guter Backtest
@@ -32604,13 +32604,13 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Alle Schnipsel lösen dasselbe Problem — das Produktionsprogramm aus Abschnitt 3.1 mit dem bekannten Optimum 530 und den Schattenpreisen 12 und 1:
Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)
+
Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)
Was dieser Anhang nicht ist. Kein Vergleich und keine Empfehlung. Welche Bibliothek für welche Aufgabe taugt, steht in Abschnitt 3.6; denselben Fall in vier Bibliotheken nebeneinander zeigt Ein_System_Vier_Ansaetze.py, die beiden Modellierungssprachen Modellierungsschichten.py. Hier geht es allein ums Nachschlagen.
D1 — SciPy: linprog und milp
-
Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.
+
Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.
Lineares Programm
import numpy as npfrom scipy.optimize import linprog
@@ -32665,7 +32665,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
D2 — HiGHS über highspy
-
Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
+
Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
import numpy as npimport highspy
@@ -32704,12 +32704,12 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Die drei häufigsten Stolpersteine
Nicht zusammen mit ortools importieren. Beide bringen eine eigene HiGHS-Kopie mit; im selben Prozess endet das in undefined symbol (Anhang C, C9). Auch cvxpy zieht highspy bei der Solver-Erkennung mit hinein.
-
Das CSR-Format stimmt oder es stimmt still nicht.starts hat so viele Einträge wie Zeilen, indices und values so viele wie Nichtnullen. Ein falscher starts-Eintrag erzeugt ein anderes, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
+
Das CSR-Format stimmt oder es stimmt still nicht.starts hat so viele Einträge wie Zeilen, indices und values so viele wie Nichtnullen. Ein falscher starts-Eintrag erzeugt ein anderes, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
output_flag abschalten, sonst überschwemmt das Solverprotokoll jede Ausgabe.
D3 — OR-Tools: pywraplp
-
Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht: Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
+
Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht:Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
from ortools.linear_solver import pywraplp# "GLOP" = LP, "SCIP" oder "CBC" = MILP, "SAT" = CP-SAT als MILP-Backend
@@ -32789,7 +32789,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
D5 — CVXPY
-
Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
+
Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
import numpy as npimport cvxpy as cp
@@ -32818,13 +32818,13 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
print("Solverfehler:", problem.status)
Die drei häufigsten Stolpersteine
-
DCP-Regeln vor dem Lösen prüfen.problem.is_dcp() und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen DCPError ohne Ort zu werfen (Anhang C, C8).
+
DCP-Regeln vor dem Lösen prüfen.problem.is_dcp() und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen DCPError ohne Ort zu werfen (Anhang C, C8).
OPTIMAL_INACCURATE ist kein Erfolg. Der Solver hat aufgegeben und meldet das leise. Diesen Fall immer eigens behandeln.
Der Aufbau kann teurer sein als das Lösen. Schleifen über Szenarien durch Vektorausdrücke ersetzen; Parameter statt Neuaufbau, wenn sich nur Zahlen ändern.
D6 — Modellierungssprachen: Pyomo und Linopy
-
Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy denkt in beschrifteten Arrays und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. Wofür nicht: ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
+
Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy denkt in beschrifteten Arrays und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. Wofür nicht: ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
Pyomo
import pyomo.environ as pyo
@@ -32906,264 +32906,747 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Anhang E: Glossar
Nachschlagewerk zu allen Fachbegriffen, Abkürzungen, Algorithmen und Bibliotheken dieses Buchs. Jeder Eintrag nennt am Ende den Abschnitt, in dem der Begriff eingeführt wird — ein Klick dorthin führt in der Online-Fassung direkt zur Erklärung im Zusammenhang. Wer einen Begriff an allen Fundstellen sucht, benutzt das Stichwortverzeichnis.
A
+
Absolutbetrag — Modellierungsmuster für |x-z|: Der Betrag selbst ist nicht linear, lässt sich aber durch eine Hilfsvariable d mit den beiden Bedingungen x-z \le d und z-x \le d ersetzen. Das funktioniert nur, wenn d minimiert wird — sonst bleibt die Schranke unausgeschöpft. → Anhang B
+
+
Adjusted Close — Um Dividenden und Splits bereinigter Schlusskurs. Nur mit ihm sind Renditen über längere Zeiträume vergleichbar; der unbereinigte Schlusskurs erzeugt an jedem Ausschüttungstag einen Scheinverlust. → Abschnitt 18.3
+
+
Aktivierungsschalter — Modellierungsmuster, das eine kontinuierliche Größe an eine Binärvariable koppelt: x \le M\,y erlaubt x > 0 nur, wenn y = 1 ist. Grundbaustein für Fixkosten, Standortentscheidungen und Mindestmengen. → Anhang B
+
+
Almgren-Chriss-Modell — Standardmodell der optimalen Orderausführung. Es löst den Zielkonflikt zwischen Marktauswirkung bei schnellem Handeln und Volatilitätsrisiko bei langsamem Handeln und liefert einen Ausführungsplan über mehrere Perioden. → Abschnitt 13.4
+
+
Alternativoptima — Mehrere Lösungen mit identischem Zielfunktionswert. Sie treten auf, wenn die Zielfunktion parallel zu einer Kante des Polyeders verläuft oder die Matrix des quadratischen Terms nur semidefinit ist. Der Solver meldet dann eine beliebige davon — zwei Läufe können unterschiedlich aussehen und trotzdem beide optimal sein. → Abschnitt 11.3
+
+
Approximate Dynamic Programming — Sammelbegriff für Verfahren, die die Wertfunktion der dynamischen Programmierung nicht exakt tabellieren, sondern annähern — etwa durch Regression oder ein neuronales Netz. Antwort auf den Fluch der Dimensionalität, wenn der Zustandsraum zu groß zum Durchzählen wird. → Abschnitt 22.9
+
B
+
+
Backtest — Rückblickender Test einer Strategie auf historischen Daten. Ein Backtest ist nur so glaubwürdig wie seine Daten, seine Kostenannahme und seine Trennung von Trainings- und Testzeitraum — Kapitel 21 zeigt die fünf Selbsttäuschungen, die ihn wertlos machen. → Kapitel 21
+
+
Bedingte Kopplung — Modellierungsmuster für „wenn A und B, dann C“. Umgesetzt über y_C \ge y_A + y_B - 1: Nur wenn beide Voraussetzungen gesetzt sind, wird die Folge erzwungen. → Anhang B
+
+
Bellman-Gleichung — Rekursionsgleichung der dynamischen Programmierung: Der Wert eines Zustands ist die Summe aus den unmittelbaren Kosten der besten Aktion und dem Wert des Folgezustands. Sie zerlegt eine mehrstufige Entscheidung in eine Kette einstufiger. → Abschnitt 13.3
+
+
Benders-Zerlegung — Dekompositionsverfahren für Probleme mit wenigen „schweren“ und vielen „leichten“ Variablen. Ein Masterproblem entscheidet über die schweren, ein Subproblem über den Rest und schickt Schnitte (Benders-Cuts) zurück, bis beide übereinstimmen. → Abschnitt 22.9
+
+
Big-M-Methode — Modellierungstrick, der logische Bedingungen über eine hinreichend große Konstante M an eine Binärvariable koppelt. M sollte so klein wie möglich gewählt werden: Große Werte weichen die Relaxation auf, verschlechtern die Laufzeit und können zu Trickle Flow führen. → Abschnitt 6.5
+
+
Binärvariable — Entscheidungsvariable mit Wertebereich \{0,1\}. Sie schaltet Fixkosten, Kapazitäten oder logische Alternativen an und aus und macht aus einem linearen Programm ein gemischt-ganzzahliges. → Abschnitt 1.6
+
+
Bipartites Matching — Zuordnung zwischen zwei disjunkten Mengen — etwa Personen und Aufgaben — mit maximalem Nutzen oder minimalen Kosten. Klassisch gelöst durch den Ungarischen Algorithmus; die Modellmatrix ist total unimodular, weshalb schon das LP ganzzahlige Ecken hat. → Abschnitt 8.4
+
+
Black-Box-Effekt — Ablehnung eines Optimierungssystems, weil seine Entscheidungen nicht nachvollziehbar sind. Gegenmittel sind ein Constraint-Trace für Einzelentscheidungen und eine Kostenzerlegung für Bewertungen. Akzeptanz ist keine Nebensache, sondern Betriebsvoraussetzung. → Abschnitt 22.3
+
+
Branch-and-Bound — Exaktes Verfahren für ganzzahlige Probleme: Der Suchraum wird rekursiv zerlegt (Branching), und ein Zweig wird verworfen, sobald seine Relaxation schlechter ist als die beste bekannte Lösung (Bounding). Ohne das Abschneiden wäre es bloßes Durchprobieren. → Abschnitt 6.4
+
+
Branch-and-Cut — Kombination aus Branch-and-Bound und Schnittebenenverfahren: In den Knoten des Suchbaums werden zusätzlich gültige Ungleichungen erzeugt, die gebrochene Lösungen abschneiden. Der Standardalgorithmus heutiger MILP-Solver. → Abschnitt 3.3
+
+
Brute Force — Vollständiges Durchprobieren aller Möglichkeiten. Für kleine Instanzen ein nützlicher Kontrollwert, in der Praxis meist chancenlos: Schon 20 binäre Entscheidungen ergeben über eine Million Kombinationen. → Abschnitt 1.3
+
+
Budgeted Uncertainty — Robustheitsansatz nach Bertsimas und Sim: Höchstens \Gamma von n Parametern nehmen gleichzeitig ihren ungünstigsten Wert an. Der Parameter \Gamma regelt stufenlos, wie vorsichtig die Lösung wird — bei \Gamma = 0 nominal, bei \Gamma = n maximal pessimistisch. → Abschnitt 12.6
+
+
Budgetlimit — Nebenbedingung, die die Summe gewichteter Entscheidungen nach oben begrenzt. Der zugehörige Schattenpreis beantwortet unmittelbar, was eine zusätzliche Budgeteinheit einbrächte. → Anhang B
+
C
+
Calmar Ratio — Jahresrendite geteilt durch den Betrag des maximalen Drawdowns. Ergänzt die Sharpe Ratio um die Verlustperspektive: Sie misst Ertrag je Einheit des schlimmsten erlebten Rückschlags statt je Einheit Schwankung. → Abschnitt 21.5
+
+
CDCL — Conflict-Driven Clause Learning — das Suchprinzip moderner SAT-Solver. Aus jedem Widerspruch wird eine Sperrklausel abgeleitet, die verhindert, dass dieselbe Sackgasse erneut betreten wird; anschließend springt die Suche gezielt zurück statt Schritt für Schritt. → Abschnitt 7.3
+
+
Chance Constraint — Nebenbedingung, die nicht immer, sondern nur mit vorgegebener Wahrscheinlichkeit gelten muss — etwa „die Versorgung reicht in 95 % der Fälle“. Bei normalverteilter Unsicherheit wird daraus eine konische Bedingung, sonst hilft eine szenariobasierte Big-M-Formulierung. → Abschnitt 12.7
+
+
Column Generation — Verfahren für Modelle mit sehr vielen Variablen: Gerechnet wird auf einer kleinen Teilmenge, und ein Preisproblem sucht wiederholt die eine Spalte, die sich noch lohnt. Erst wenn keine mehr existiert, ist das Optimum bewiesen. → Abschnitt 9.8
+
+
Conditional Value at Risk (CVaR) — Erwarteter Verlust in den schlechtesten Szenarien jenseits des Value at Risk. Anders als der VaR ist der CVaR kohärent und subadditiv und lässt sich nach Rockafellar und Uryasev exakt als lineares Programm formulieren. → Abschnitt 20.4
+
+
Conflict Learning — Technik moderner SAT- und CP-Solver, aus jedem erkannten Widerspruch eine Sperrklausel abzuleiten. Der Solver lernt so aus Fehlschlägen, statt gleichartige Sackgassen immer wieder zu durchsuchen. → Abschnitt 7.3
+
+
Constraint Attribution — Diagnoseverfahren für den häufigsten Praxisfall: Das Modell rechnet, der Plan ist zulässig — und trotzdem enttäuschend. Jede Bedingungsgruppe wird einzeln gelockert und gemessen, wie sich der Zielwert ändert. Dasselbe Vorgehen wie der Deletion Filter, nur mit Kosten statt Zulässigkeit als Kriterium. → Abschnitt 22.4
+
+
Constraint Programming (CP) — Paradigma, das nicht über Zielfunktionsgradienten, sondern über logische Verträglichkeit sucht: Constraints schränken Wertebereiche ein, bis eine zulässige Belegung gefunden ist. Stark bei Reihenfolgen, Schichtplänen und Ressourcenkonflikten. → Abschnitt 7.3
+
+
Constraint Propagation — Kernmechanismus von CP-Solvern: Aus einer Zuweisung werden unmögliche Werte anderer Variablen sofort entfernt. Der Suchbaum schrumpft dadurch, bevor er überhaupt durchsucht wird. Genau hier liegt der Vorteil globaler Constraints — ihre Propagatoren schließen weit mehr Werte aus als die gleichwertige Menge einzelner Ungleichungen. → Abschnitt 7.3
+
+
Constraint-Trace — Nachvollziehbare Begründung einer einzelnen Solver-Entscheidung — „Warum bekommt Person A die Stunde und nicht Person B?“ — anhand der bindenden Bedingungen und Strafpunkte. Das Gegenmittel zum Black-Box-Effekt. → Abschnitt 22.3
+
+
CP-SAT — Constraint-Programming-Solver von OR-Tools. Er übersetzt ein Modell in boolesche Erfüllbarkeit und löst es mit Propagation, Conflict Learning und paralleler Suche. Für Reihenfolge- und Zuordnungsprobleme meist schneller als ein MILP-Solver. → Abschnitt 7.3
+
+
CP-SAT-Solver — Die Solver-Komponente von OR-Tools, angesprochen über cp_model.CpSolver(). Sie liefert neben der Lösung Statuscode, Laufzeit und die beste bekannte Schranke und lässt sich über num_workers auf mehrere Kerne verteilen. → Abschnitt 7.3
+
+
CSR-Format — Compressed Sparse Row — Speicherformat für dünn besetzte Matrizen, das nur die von null verschiedenen Einträge samt ihrer Positionen ablegt. Für Optimierungsmodelle mit zehntausenden Nebenbedingungen der Unterschied zwischen passt und passt nicht in den Speicher. → Abschnitt 3.5
+
+
CVRPTW — Capacitated Vehicle Routing Problem with Time Windows — Tourenplanung mit einer Flotte, Kapazitätsgrenzen je Fahrzeug und Zeitfenstern je Kunde. Die praxisnahe Variante des VRP und Standardfall der Routing-Bibliothek von OR-Tools. → Abschnitt 8.5
+
+
CVXPY — Modellierungssprache für konvexe Optimierung. Sie schreibt das Problem in mathematiknaher Notation, prüft die Konvexität nach den Regeln der Disciplined Convex Programming und reicht es an einen passenden Solver weiter. → Abschnitt 3.4
+
D
+
Data Snooping — Wiederholtes Ausprobieren von Strategien auf denselben Daten, bis eine gut aussieht. Der Fund ist dann eine Eigenschaft der Stichprobe, nicht des Marktes — gefährlicher als ein Programmierfehler, weil das Ergebnis plausibel wirkt. → Abschnitt 21.6
+
+
+
DCP (Disciplined Convex Programming) — Regelwerk von CVXPY, das jede Variable als konkav, konvex oder affin ausweist und aus der Kombination der Bausteine die Konkavität/Konvexität der Zielfunktion und der Nebenbedingungen erschließt. Kapitel 11 erklärt es; ein DCPError meldet eine Verletzung. → Kapitel 11
+
+
DCPError — Fehlermeldung von CVXPY, wenn ein Ausdruck die Regeln der Disciplined Convex Programming verletzt. Sie besagt nicht, dass das Problem unlösbar ist, sondern dass CVXPY die Konvexität nicht nachweisen kann — meist hilft eine äquivalente Umformung. → Anhang C
+
+
Deletion Filter — Verfahren zur Eingrenzung von Unlösbarkeit. Es fragt nicht „ist diese Bedingung schuld?“, sondern „wird diese Bedingung für den Widerspruch überhaupt gebraucht?“, und findet dadurch auch dann eine minimale unlösbare Teilmenge, wenn sich mehrere Widersprüche überlagern. → Anhang C
+
+
Diskrete Rendite — Einfache Rendite (P_t - P_{t-1})/P_{t-1}. Sie ist über Titel additiv und deshalb die richtige Wahl für Portfoliogewichte — anders als die logarithmische Rendite, die über die Zeit additiv ist. → Abschnitt 18.3
+
+
Diversifikation — Risikoreduktion durch Verteilung auf Titel, deren Renditen nicht vollständig gleichlaufen. Der Effekt steckt vollständig in den Kovarianzen: Bei Korrelation 1 verschwindet er, bei negativer Korrelation ist er am größten. → Abschnitt 19.3
+
+
DTO (Data Transfer Object) — Schlichtes Objekt, das nur Daten transportiert und keine Logik enthält. Im Buch trägt die Loesung-Klasse in or_kern.py das Ergebnis eines Solverlaufs so aus der Rechenschicht heraus. → Abschnitt 3.5
+
+
Dual Simplex — Variante des Simplex-Verfahrens, die eine dual zulässige Basis beibehält und die primale Zulässigkeit herstellt. Das Verfahren der Wahl beim Warm-Start, etwa in jedem Knoten eines Branch-and-Bound-Baums. → Abschnitt 3.3
+
+
Duales Problem — Das jedem Optimierungsproblem zugeordnete Gegenstück, dessen Variablen die Nebenbedingungen des Originals bepreisen. Seine Optimalwerte sind die Schattenpreise; im Optimum stimmen beide Zielfunktionswerte überein. → Abschnitt 5.6
+
+
Dualitätstheorie — Theorie des Zusammenhangs zwischen einem Optimierungsproblem und seinem Dualproblem. Der schwache Dualitätssatz liefert stets eine Schranke, der starke besagt, dass bei linearen Programmen beide Optima zusammenfallen. → Abschnitt 5.6
+
+
Dynamische Programmierung (DP) — Lösungsprinzip für mehrstufige Entscheidungen: Das Problem wird in Zustände und Stufen zerlegt und rückwärts gelöst, wobei jeder Zustand nur einmal bewertet wird. Voraussetzung ist das Optimalitätsprinzip. → Abschnitt 13.3
+
E
+
Ecke (Extrempunkt) — Punkt des zulässigen Bereichs, der sich nicht als echte Mischung zweier anderer zulässiger Punkte schreiben lässt. Nach dem Fundamentalsatz der linearen Optimierung liegt in einer Ecke stets ein Optimum — deshalb sucht der Simplex nur dort. → Abschnitt 2.4
+
+
Effizienzgrenze (Efficient Frontier) — Kurve aller Portfolios, die zu gegebenem Risiko die höchste erwartete Rendite liefern. Portfolios darunter sind dominiert, Portfolios darüber unerreichbar. → Abschnitt 19.4
+
+
Entartung — Zustand, in dem mehr Nebenbedingungen aktiv sind, als Variablen frei bleiben. Der Simplex kann dann Basiswechsel ohne Zielwertverbesserung durchführen, und die Schattenpreise sind nicht mehr eindeutig — zwei Solver dürfen verschiedene, gleichermaßen korrekte Dualwerte melden. → Abschnitt 5.9
+
+
Entscheidungsvariable — Die vom Solver frei wählbare Größe eines Modells. Ihr Wertebereich — kontinuierlich, ganzzahlig oder binär — bestimmt die Problemklasse und damit den Lösungsaufwand. → Abschnitt 1.6
+
+
Entscheidungsvektor — Zusammenfassung aller Entscheidungsvariablen zu einem Vektor \mathbf{x}. Erst diese Schreibweise macht die kompakte Matrixform eines Modells möglich. → Abschnitt 2.3
+
+
Entweder-Oder-Bedingung — Modellierungsmuster für sich ausschließende Alternativen: Genau eine von mehreren Bedingungen soll gelten. Umgesetzt über Binärvariablen mit Summe 1 und Big-M-Kopplung an die jeweilige Bedingung. → Anhang B
+
+
Error-Maximizer-Effekt — Eigenschaft der Mean-Variance-Optimierung, Schätzfehler zu verstärken statt auszugleichen. Der Optimierer sucht gezielt die Richtungen mit der niedrigsten geschätzten Varianz — und das sind genau jene, deren Varianz am stärksten unterschätzt wurde. → Abschnitt 18.4
+
+
EVPI (Expected Value of Perfect Information) — Differenz zwischen den erwarteten Kosten unter Unsicherheit und den Kosten bei perfektem Wissen. Obergrenze für den Wert jeder Prognoseverbesserung: Keine noch so gute Vorhersage kann mehr einbringen. → Abschnitt 12.5
+
+
Exklusiv-Oder — Modellierungsmuster für „genau eine von zwei Optionen“: y_A + y_B = 1. Die Abgrenzung zum einfachen Oder (y_A + y_B \ge 1) ist in der Praxis eine häufige Fehlerquelle. → Anhang B
+
+
Expected Shortfall — Anderer Name für den Conditional Value at Risk; in der Bankenregulierung die gebräuchliche Bezeichnung. Beide meinen den erwarteten Verlust jenseits des VaR-Quantils. → Abschnitt 20.4
+
+
Explainable OR — Nachvollziehbarmachung von Solver-Ergebnissen über Schattenpreise, aktive Restriktionen, Constraint-Trace und Kostenzerlegung. Das Gegenstück zu Explainable AI für Optimierungsmodelle. → Abschnitt 22.3
+
F
+
Fallback-Strategie — Festgelegtes Vorgehen für den Fall, dass der Solver kein Ergebnis liefert: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Niemals: nichts. → Abschnitt 22.5
+
+
Falsche Dualwerte — Fehlerbild, bei dem Schattenpreise unplausibel wirken. Zwei Ursachen dominieren: Entartung macht sie mehrdeutig, und ganzzahlige Modelle haben streng genommen gar keine Dualwerte — was ein MILP-Solver zurückgibt, gehört zur Relaxation des letzten Knotens. → Anhang C
+
+
Fat Tails — Verteilungsränder, die dicker auslaufen als bei der Normalverteilung. Extremereignisse sind dann deutlich häufiger als das Normalmodell vorhersagt — der Grund, warum varianzbasierte Risikomaße das Verlustrisiko unterschätzen. → Abschnitt 20.3
+
+
Fluch der Dimensionalität — Exponentielles Wachstum des Zustandsraums mit jeder zusätzlichen Zustandsdimension. Es begrenzt die dynamische Programmierung und motiviert näherungsweise Verfahren. → Abschnitt 13.5
+
+
Fluch des Durchschnitts (Flaw of Averages) — Systematischer Fehler beim Planen mit Erwartungswerten statt mit Verteilungen. Das Ergebnis der Durchschnittsrechnung ist im Allgemeinen nicht der Durchschnitt der Ergebnisse; formal folgt das aus der Jensenschen Ungleichung. → Abschnitt 12.3
+
+
Flusserhaltung — Bedingung, dass an jedem Knoten Abfluss minus Zufluss dem Saldo des Knotens entspricht — das Kirchhoff-Gesetz der Netzwerkoptimierung. Summieren sich die Salden nicht zu null, ist das Problem grundsätzlich unlösbar. → Abschnitt 8.3
+
+
Fundamentalsatz der linearen Optimierung — Das Optimum eines lösbaren linearen Programms wird stets in mindestens einer Ecke des zulässigen Polyeders angenommen. Die Rechtfertigung dafür, nur endlich viele Ecken statt unendlich vieler Punkte zu prüfen. → Abschnitt 2.4
+
G
+
Gemischt-ganzzahlige Optimierung (MILP) — Mixed-Integer Linear Programming — lineares Modell mit mindestens einer ganzzahligen oder binären Variablen. Die Ganzzahligkeit macht das Problem NP-schwer; gelöst wird es mit Branch-and-Cut. → Abschnitt 6.3
+
+
Genetischer Algorithmus — Metaheuristik, die eine Population von Lösungen durch Selektion, Rekombination und Mutation weiterentwickelt. Sie gibt keine Optimalitätsgarantie, kommt dafür mit Zielfunktionen zurecht, die weder linear noch differenzierbar sind. → Abschnitt 9.8
+
+
Gestaffelte Preise — Modellierungsmuster für Mengenrabatte und Tarifstufen. Die Menge wird in Segmente zerlegt, jedes mit eigenem Preis; Binärvariablen erzwingen, dass ein Segment erst genutzt wird, wenn das vorhergehende ausgeschöpft ist. Ohne diese Reihenfolgebedingung sucht sich der Solver einfach das billigste Segment heraus. → Anhang B
+
+
GIL (Global Interpreter Lock) — Sperre des CPython-Interpreters, die verhindert, dass zwei Threads gleichzeitig Python-Code ausführen. Sie ist der Grund, warum echte Parallelität in Python über Prozesse statt Threads läuft — Solver umgehen sie, weil sie in C++ rechnen. → Notation und Abkürzungen
+
+
Gini-Koeffizient — Maß für die Ungleichverteilung einer Größe zwischen 0 (alle gleich) und 1 (einer bekommt alles). Im Buch als Fairnesskennzahl für Wunscherfüllungsquoten verwendet. → Projektwerkstatt
+
+
Gleitendes Fenster — Auswertung, bei der ein Zeitfenster fester Länge über die Daten geschoben wird. Grundlage rollierender Schätzungen und des Walk-Forward-Backtests. → Anhang B
+
+
Global Minimum Variance Portfolio (GMV) — Portfolio kleinstmöglicher Varianz. Es benötigt keine Renditeprognose und ist deshalb deutlich robuster gegen Schätzfehler als das renditemaximierende Mean-Variance-Portfolio. → Abschnitt 19.4
+
+
Globale Constraints — Vorgefertigte Bausteine wie AllDifferent, NoOverlap oder Cumulative, die häufige Strukturen kompakt ausdrücken. Ihr eigentlicher Wert sind die spezialisierten Propagatoren, die weit mehr Werte ausschließen als die gleichwertige Menge einzelner Ungleichungen. → Abschnitt 7.4
+
+
Globales Minimum — Der kleinste Zielfunktionswert im gesamten zulässigen Bereich. Bei konvexen Problemen ist jedes lokale Minimum zugleich global — der Grund, warum Konvexität die Grenze zwischen leicht und schwer markiert. → Abschnitt 2.5
+
+
GLOP — Google Linear Optimization Package — der LP-Solver in OR-Tools. Er löst rein kontinuierliche Probleme; sobald Ganzzahligkeit ins Spiel kommt, übernimmt CP-SAT oder ein MILP-Backend. → Notation und Abkürzungen
+
+
Goldene Regel des Backtestens — Zum Zeitpunkt t darf nur Information verwendet werden, die zu t vorlag. Der Selbsttest mit absichtlich manipulierter Zukunft prüft das automatisch: Wird der Backtest dadurch besser, liegt ein Lookahead-Bias vor. → Abschnitt 21.12
+
+
Gradient — Vektor der partiellen Ableitungen einer Funktion; er zeigt in Richtung des steilsten Anstiegs. Gradientenbasierte Verfahren wie SLSQP nutzen ihn, um sich schrittweise an ein Optimum heranzutasten. → Abschnitt 11.2
+
+
+
Graphen — Mathematische Struktur aus Knoten und Kanten; das Modellierungsmittel für Netzwerke, Touren und Flüsse. Kapitel 8 behandelt Min-Cost-Flow, Matching und das Vehicle Routing Problem als Graphanwendungen. → Kapitel 8
+
+
Gurobi — Kommerzieller Hochleistungssolver für LP, MILP und QP. Im Buch nicht vorausgesetzt, aber als Referenzpunkt genannt: Über die Modellierungsschicht lässt er sich meist durch Ändern eines einzigen Arguments einsetzen. → Abschnitt 6.4
+
H
+
Herfindahl-Index — Summe der quadrierten Anteile; Maß für Konzentration. Sein Kehrwert ist die effektive Titelzahl und beantwortet, auf wie viele gleich große Positionen ein Portfolio praktisch verteilt ist. → Projektwerkstatt
+
+
Hierarchische Relaxation — Gegenmittel gegen Unlösbarkeit im Betrieb: Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. Der Solver liefert dann immer einen Plan und zeigt zugleich präzise, wo es klemmt. → Abschnitt 22.3
+
+
HiGHS — Offener Hochleistungssolver für LP, MILP und QP. Backend von scipy.optimize.linprog und CVXPY, direkt ansprechbar über highspy. → Abschnitt 3.4
+
I
+
IIS (Irreducible Infeasible Subset) — Minimale Menge von Nebenbedingungen, die zusammen unlösbar sind, deren echte Teilmengen aber alle lösbar wären. Das präziseste Ergebnis, das eine Unlösbarkeitsdiagnose liefern kann. → Anhang C
+
+
Implikation — Modellierungsmuster für „wenn A, dann B“: y_B \ge y_A. Die Umkehrung gilt dabei ausdrücklich nicht — B darf auch ohne A gesetzt sein. → Anhang B
+
+
Importfehler zwischen ortools und highspy — Absturz beim Import beider Bibliotheken im selben Prozess: Sie bringen je eine eigene HiGHS-Kopie mit, deren C++-Symbole kollidieren. Abhilfe ist die Trennung in getrennte Prozesse — CVXPY zieht ein installiertes highspy dabei ungefragt mit hinein. → Anhang C
+
+
Infeasibility — Zustand eines Modells ohne jede zulässige Lösung. Im Betrieb über Schlupfvariablen mit hohem Strafgewicht aufzufangen, in der Diagnose über den Deletion Filter einzugrenzen. → Abschnitt 22.3
+
+
Instabile Lösung — Fehlerbild, bei dem winzige Datenänderungen die Lösung völlig umwerfen. Meist eine Folge schlechter Konditionierung; die Konditionszahl zeigt es an, die Ruiz-Equilibrierung behebt es. → Anhang C
+
+
Interior-Point-Verfahren — Lösungsverfahren, das sich durch das Innere des zulässigen Bereichs zum Optimum bewegt, statt Ecken abzulaufen. Bei sehr großen Modellen oft schneller als der Simplex, liefert dafür keine Basislösung und damit weniger brauchbare Sensitivitätsinformationen. → Abschnitt 3.3
+
+
Intervallvariable — Variable mit Start, Dauer und Ende, die in CP-SAT eine Aktivität beschreibt. Sie ist die Voraussetzung für Überschneidungsverbote und Ressourcenprofile. → Abschnitt 7.4
+
J
+
Jensensche Ungleichung — Für konvexe Funktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]). Die formale Grundlage des Fluchs des Durchschnitts: Mit Mittelwerten zu rechnen unterschätzt systematisch die erwarteten Kosten. → Abschnitt 12.3
+
+
Job-Shop-Scheduling — Ablaufplanung, bei der mehrere Aufträge in je eigener Reihenfolge über dieselben Maschinen laufen. Klassisches Anwendungsfeld von CP-SAT; Zielgröße ist meist der Makespan. → Abschnitt 7.6
+
K
+
Kanonische Standardform — Einheitliche Matrixschreibweise eines LP als \min \mathbf{c}^\top\mathbf{x} unter den Bedingungen \mathbf{A}\mathbf{x} \le \mathbf{b} und \mathbf{x} \ge 0. Jedes lineare Modell lässt sich darauf bringen — Solver erwarten genau diese Form. → Abschnitt 2.3
+
+
Kapazitätsvektor — Der Vektor \mathbf{b} der rechten Seiten in \mathbf{A}\mathbf{x} \le \mathbf{b}. Seine Einträge sind die verfügbaren Ressourcen, und die zugehörigen Schattenpreise sagen, was eine zusätzliche Einheit davon wert wäre. → Abschnitt 2.3
+
+
Kardinalitätsbeschränkung — Obergrenze für die Anzahl gleichzeitig aktiver Entscheidungen, formuliert über die Summe der zugehörigen Binärvariablen. Typisch für „höchstens zehn Titel im Depot“. → Abschnitt 6.5
+
+
Karush-Kuhn-Tucker-Bedingungen (KKT) — Notwendige Optimalitätsbedingungen restringierter Probleme: Stationarität, primale und duale Zulässigkeit sowie komplementärer Schlupf. Bei konvexen Problemen sind sie zugleich hinreichend und damit ein vollständiger Optimalitätsnachweis. → Abschnitt 11.4
+
+
Kohärentes Risikomaß — Risikomaß mit den vier Eigenschaften Monotonie, Subadditivität, positive Homogenität und Translationsinvarianz. Der CVaR erfüllt sie, der VaR nicht — dessen Verletzung der Subadditivität kann Diversifikation rechnerisch bestrafen. → Abschnitt 20.4
+
+
Kombinatorische Explosion — Überproportionales Wachstum der Zahl möglicher Lösungen mit der Problemgröße. Sie ist der Grund, warum systematisches Ausprobieren schon bei kleinen Instanzen aussichtslos wird. → Abschnitt 1.4
+
+
Komplementärer Schlupf — Bedingung s_i \cdot y_i = 0: Eine Nebenbedingung ist entweder aktiv (kein Schlupf) oder ihr Schattenpreis verschwindet. Wirtschaftlich gelesen: Nur knappe Ressourcen haben einen Preis. → Abschnitt 5.6
+
+
Konditionszahl — Verhältnis von größtem zu kleinstem Singulärwert einer Matrix. Sie misst, wie stark kleine Datenänderungen die Lösung verzerren können; hohe Werte kündigen numerische Probleme an, lange bevor der Solver etwas meldet. → Abschnitt 18.6
+
+
Konische Optimierung — Erweiterung der linearen Optimierung auf Kegelbedingungen. Der Second-Order Cone (SOCP) erfasst Normen und damit Risikoschranken, die semidefinite Variante (SDP) Bedingungen an Matrizen. Beide bleiben konvex und damit global lösbar. → Abschnitt 12.7
+
+
Konvexität — Eigenschaft, bei der jede Verbindungsstrecke zweier Punkte innerhalb der Menge bzw. unterhalb des Funktionsgraphen liegt. Bei konvexen Problemen ist jedes lokale Optimum global — die eigentliche Grenze zwischen leicht und schwer lösbar. → Abschnitt 2.5
+
+
Korn-Transformation — Umformung der nicht-konvexen Sharpe-Ratio-Maximierung in ein konvexes quadratisches Programm durch Homogenisierung. Sie macht aus einem Bruch eine Skalierung und damit aus einem unlösbaren ein lösbares Problem. → Abschnitt 19.4
+
+
Kostenzerlegung — Aufschlüsselung eines Zielfunktionswerts in seine Bestandteile — etwa Alpha-Ertrag, Risikoprämie und Transaktionskostenstrafe. Sie beantwortet die Frage „woraus besteht dieses Ergebnis?“ und ist ein Baustein der Erklärbarkeit. → Abschnitt 22.3
+
+
Kovarianzmatrix — Matrix der paarweisen Kovarianzen aller Titel. Sie steuert im Markowitz-Modell den Diversifikationseffekt und ist bei vielen Titeln und wenigen Beobachtungen notorisch schlecht konditioniert. → Abschnitt 18.4
+
+
Kumulative Ressource — Ressource mit begrenzter Kapazität, die mehrere Aktivitäten gleichzeitig nutzen können — etwa vier Monteure. In CP-SAT über AddCumulative ausgedrückt. → Anhang B
+
L
+
Lagrange-Funktion — Zielfunktion zuzüglich der mit Multiplikatoren gewichteten Nebenbedingungen. Ihre Stationaritätsbedingung ist die erste der KKT-Bedingungen und die Brücke zwischen restringierter und freier Optimierung. → Abschnitt 11.4
+
+
Lagrange-Multiplikator — Gewicht, mit dem eine Nebenbedingung in die Lagrange-Funktion eingeht. Sein Optimalwert ist der Schattenpreis der Bedingung. → Abschnitt 11.4
+
+
Large Neighborhood Search (LNS) — Metaheuristik, die wiederholt einen größeren Teil der aktuellen Lösung zerstört und exakt neu aufbaut. Sie verbindet die Reichweite heuristischer Sprünge mit der Genauigkeit eines Solvers auf dem Teilproblem. → Abschnitt 9.7
+
+
Laufzeitexplosion — Fehlerbild, bei dem ein Modell nach einer kleinen Erweiterung nicht mehr fertig rechnet. Häufige Ursachen sind ein zu großes M, fehlende Symmetriebrechung und eine schwache Relaxation. → Anhang C
+
+
Ledoit-Wolf-Shrinkage — Schrumpfung der Stichprobenkovarianz in Richtung eines strukturierten Ziels; der optimale Mischungsfaktor wird analytisch bestimmt. scikit-learn verwendet die skalierte Einheitsmatrix als Ziel. → Abschnitt 18.5
+
+
Lexikografische Optimierung — Mehrzielverfahren mit strenger Rangfolge: Das wichtigste Ziel wird optimiert, sein Wert festgeschrieben, dann das zweitwichtigste — und so fort. Kein Ziel darf zugunsten eines nachrangigen verschlechtert werden. → Abschnitt 14.7
+
+
Lineare Programmierung (LP) — Linear Programming — Optimierung einer linearen Zielfunktion unter linearen Nebenbedingungen mit kontinuierlichen Variablen. Die am besten verstandene Problemklasse des Operations Research: in Polynomialzeit lösbar und mit vollständiger Dualitätstheorie. → Abschnitt 5.3
+
+
Linopy — Modellierungsbibliothek, die Optimierungsmodelle über beschriftete Achsen aufbaut (xarray-Stil). Ihre Stärke sind große, regelmäßig indizierte Modelle wie Energiesystemrechnungen. → Abschnitt 3.7
+
+
linprog — Funktion scipy.optimize.linprog zum Lösen linearer Programme. Sie erwartet das Modell in kanonischer Standardform als Matrizen und benutzt seit SciPy 1.11 standardmäßig HiGHS als Backend. → Abschnitt 5.7
+
+
Logarithmische Rendite — Stetige Rendite als Logarithmus des Preisverhältnisses. Sie ist über die Zeit additiv und deshalb die richtige Wahl für Zeitreihenaggregation — nicht aber für die Gewichtung im Portfolio. → Abschnitt 18.3
+
+
Lokales Minimum — Punkt, der in seiner Umgebung optimal ist, global aber übertroffen werden kann. Bei nichtkonvexen Problemen liefern gradientenbasierte Verfahren nur solche Punkte; ein Multistart macht das Problem sichtbar. → Abschnitt 2.5
+
+
Lookahead-Bias — Fehler, bei dem Informationen in eine Entscheidung einfließen, die zum Entscheidungszeitpunkt noch nicht vorlagen. Die häufigste Ursache für Backtests, die zu gut aussehen, um wahr zu sein. → Abschnitt 21.6
+
+
LP-Relaxation — Das ganzzahlige Modell ohne die Ganzzahligkeitsforderung. Ihre Lösung ist schnell zu berechnen und liefert eine Schranke: Besser als der Relaxationswert kann die ganzzahlige Lösung nicht werden. Genau darauf beruht das Abschneiden im Branch-and-Bound. → Abschnitt 6.4
+
M
+
Makespan — Gesamtdurchlaufzeit eines Ablaufplans: der Zeitpunkt, zu dem die letzte Aktivität endet. Standardzielgröße im Job-Shop-Scheduling. → Abschnitt 7.6
+
+
MAPE — Mean Absolute Percentage Error — mittlerer absoluter prozentualer Prognosefehler. Beliebt, weil einheitenfrei, aber unbrauchbar bei Werten nahe null. → Abschnitt 15.3
+
+
Marchenko-Pastur-Gesetz — Ergebnis der Zufallsmatrizentheorie über die Eigenwertverteilung von Stichproben-Kovarianzmatrizen. Es zeigt, dass die kleinsten Eigenwerte systematisch unterschätzt und die größten überschätzt werden — die theoretische Wurzel des Error-Maximizer-Effekts. → Abschnitt 18.4
+
+
Markowitz-Modell (Mean-Variance) — Rahmenwerk, das ein Portfolio über das Zusammenspiel von erwarteter Rendite und Kovarianz bewertet. Es begründete die moderne Portfoliotheorie und ist zugleich das Lehrbeispiel für die Empfindlichkeit gegenüber Schätzfehlern. → Abschnitt 19.4
+
+
Marktauswirkung (Market Impact) — Preisverschlechterung, die eine eigene Order durch ihr Volumen auslöst. Sie wächst überproportional mit der Handelsgeschwindigkeit und ist der Grund, große Orders über die Zeit zu strecken. → Abschnitt 13.4
+
+
Matplotlib — Standardbibliothek für statische Diagramme in Python. Im Buch erzeugt sie alle Abbildungen der Druckfassung; die interaktiven Fassungen der Website stammen von Plotly. → Abschnitt 3.4
+
+
Matrixform — Kompakte Schreibweise eines Modells über Vektoren und Matrizen statt einzelner Gleichungen. Sie ist nicht nur kürzer, sondern die Form, die Solver tatsächlich entgegennehmen. → Abschnitt 2.12
+
+
Matrix-Vektor-Produkt — Rechenoperation \mathbf{A}\mathbf{x}, die jede Zeile der Matrix mit dem Vektor multipliziert. In der Optimierung berechnet sie den Ressourcenverbrauch aller Nebenbedingungen in einem Schritt. → Abschnitt 2.3
+
+
Maximin-Fairness — Zielsetzung „der am schlechtesten Gestellte soll möglichst gut dastehen“. Modelliert über eine Hilfsvariable t mit t \le f_i(\mathbf{x}) für alle i und Maximierung von t. → Anhang B
+
+
Maximum Drawdown — Größter prozentualer Rückgang vom bisherigen Höchststand. Anders als die Volatilität misst er den tatsächlich erlittenen Verlustweg und ist deshalb die Kennzahl, an der Anleger aussteigen. → Abschnitt 21.5
+
+
Mensch in der Schleife — Betriebsprinzip, bei dem der Optimierer vorschlägt und ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab, bevor sie wirksam werden. → Abschnitt 22.5
+
+
Metaheuristik — Übergeordnetes Suchschema wie Simulated Annealing, Tabu-Suche oder genetische Algorithmen, das auf viele Problemtypen anwendbar ist. Es liefert gute Lösungen ohne Optimalitätsbeweis und ist dort angebracht, wo exakte Verfahren an der Größe scheitern. → Abschnitt 9.3
+
+
Min-Cost-Flow-Problem — Kostengünstigster Transport durch ein Netzwerk unter Kapazitäts- und Flusserhaltungsbedingungen. Die Modellmatrix ist total unimodular, weshalb schon die LP-Lösung ganzzahlig ausfällt. → Abschnitt 8.3
+
+
Mindestabnahmemenge — Bedingung „entweder gar nichts oder mindestens m“. Sie ist nicht linear und braucht eine Binärvariable: m\,y \le x \le M\,y. → Anhang B
+
+
MINLP — Mixed-Integer Nonlinear Programming — Modelle mit ganzzahligen Variablen und nichtlinearen Funktionen. Die schwierigste der Standardklassen: Sie vereint die kombinatorische Explosion mit dem Problem lokaler Optima. → Abschnitt 11.6
+
+
MIP-Gap — Relativer Abstand zwischen der besten gefundenen Lösung und der besten bekannten Schranke. Er beziffert, wie viel im schlechtesten Fall noch fehlt — ein Gap von 2 % heißt: höchstens 2 % besser ist überhaupt möglich. → Abschnitt 22.3
+
+
MIQP — Mixed-Integer Quadratic Programming — quadratische Zielfunktion mit ganzzahligen Variablen. Der Fall, der entsteht, sobald ein Portfoliomodell eine Obergrenze für die Zahl der Titel bekommt. → Notation und Abkürzungen
+
+
Monte-Carlo-Simulation — Erzeugung vieler Zufallsszenarien, um Kennzahlen empirisch zu schätzen. Sie bewertet eine gegebene Entscheidung, optimiert aber nicht — der Unterschied wird in der Praxis häufig übersehen. → Abschnitt 12.4
+
+
MSE — Mean Squared Error — mittlerer quadratischer Fehler. Das Standardmaß der Prognosegüte; es bestraft große Abweichungen überproportional und ist nicht dasselbe wie geringe Entscheidungskosten. → Notation und Abkürzungen
+
+
MTZ-Formulierung — Miller-Tucker-Zemlin-Bedingungen, die über Rangvariablen Kurzzyklen ausschließen. Kompakt in der Zahl der Nebenbedingungen, dafür mit schwacher Relaxation — der klassische Zielkonflikt bei Tourenmodellen. → Abschnitt 8.5
+
+
Multi-Objective Optimization — Optimierung mit mehreren, konkurrierenden Zielen. Statt einer besten Lösung gibt es eine Pareto-Front; die Auswahl daraus ist eine Entscheidung, keine Rechnung. → Abschnitt 22.9
+
N
+
Nebenbedingung (Constraint) — Gleichung oder Ungleichung, die zulässige von unzulässigen Lösungen trennt. Harte Bedingungen müssen erfüllt sein, weiche werden über Strafterme lediglich bepreist. → Abschnitt 1.6
+
+
Newsvendor-Problem — Klassisches Einperiodenmodell: Wie viel bestellt man bei unsicherer Nachfrage, wenn Überbestand und Fehlmenge unterschiedlich teuer sind? Die Lösung ist ein Quantil des Nachfragemodells, nicht ihr Erwartungswert. → Abschnitt 12.1
+
+
Nichtlineare Programmierung (NLP) — Nonlinear Programming — Problemklasse mit nichtlinearer Ziel- oder Nebenbedingungsfunktion. Ohne Konvexität liefern Verfahren wie SLSQP nur lokale Optima. → Abschnitt 11.5
+
+
Nichtüberlappung — Bedingung, dass sich Aktivitäten auf derselben Ressource zeitlich nicht überschneiden. In CP-SAT über AddNoOverlap auf Intervallvariablen ausgedrückt. → Anhang B
+
+
NP-schwer — Komplexitätsklasse von Problemen, für die kein Algorithmus mit polynomialer Laufzeit bekannt ist. Praktisch heißt das: Die Lösungszeit kann mit der Problemgröße explodieren — nicht aber, dass jede Instanz schwer wäre. → Abschnitt 6.4
+
+
NumPy — Grundbibliothek für numerisches Rechnen in Python. Sie liefert das Array, mit dem Vektoren und Matrizen eines Modells überhaupt erst kompakt und schnell aufgebaut werden können. → Abschnitt 3.4
+
O
+
Operations Research (OR) — Disziplin, die reale Entscheidungsprobleme in mathematische Modelle überführt und mit exakten oder heuristischen Algorithmen löst. Sie beantwortet nicht „was wird geschehen?“, sondern „was sollen wir tun?“. → Abschnitt 1.3
+
+
Optimalitätsprinzip — Grundsatz von Bellman: Jede Teilpolitik einer optimalen Politik ist ihrerseits optimal für den erreichten Zustand. Ohne ihn wäre die Rückwärtsinduktion nicht zulässig. → Abschnitt 13.3
+
+
OR-Tools — Optimierungssammlung von Google mit CP-SAT, einer Routing-Bibliothek und Anbindungen an LP/MILP-Solver. Im Buch die erste Wahl für Reihenfolge-, Zuordnungs- und Tourenprobleme. → Abschnitt 1.2
+
+
Overfitting — Anpassung eines Modells an Zufälligkeiten der Trainingsdaten statt an die zugrunde liegende Struktur. Im Backtesting die Regel, nicht die Ausnahme — deshalb der Walk-Forward-Test. → Abschnitt 21.6
+
P
+
pandas — Bibliothek für tabellarische Daten mit Zeit- und Beschriftungsindex. Im Buch der übliche Weg von Roh- zu Modelldaten, besonders in den Finanzkapiteln. → Abschnitt 3.4
+
+
Parameter — Feststehende Größe eines Modells — Kosten, Kapazitäten, Bedarfe. Parameter werden nicht optimiert, sondern gegeben; ihre Unsicherheit ist der Gegenstand der stochastischen und robusten Optimierung. → Abschnitt 1.6
+
+
Pareto-Front — Menge aller Lösungen, bei denen kein Ziel verbessert werden kann, ohne ein anderes zu verschlechtern. Sie zeigt den Zielkonflikt als Kurve, statt ihn in einer Gewichtung zu verstecken. → Abschnitt 14.3
+
+
Pivotisierung — Basiswechsel im Simplex-Verfahren: Eine Variable verlässt die Basis, eine andere tritt ein. Jeder Pivotschritt entspricht dem Weiterwandern zur benachbarten Ecke des Polyeders. → Abschnitt 5.4
+
+
Plotly — Bibliothek für interaktive Diagramme im Browser. Die Website des Buchs bindet damit Gantt-Diagramme, Effizienzlinien und Pareto-Fronten zum Zoomen und Erkunden ein. → Abschnitt 3.4
+
+
Polars — Schnelle DataFrame-Bibliothek mit spaltenorientiertem Speicher und verzögerter Auswertung. Im Buch als Alternative zu pandas dort erwähnt, wo der Modellaufbau selbst zum Engpass wird. → Abschnitt 3.8
+
+
Polyeder — Schnittmenge endlich vieler Halbräume — die geometrische Gestalt des zulässigen Bereichs eines linearen Programms. Seine Ecken sind die Kandidaten für das Optimum. → Abschnitt 2.4
+
+
Positiv (semi-)definit — Eigenschaft einer symmetrischen Matrix, deren Eigenwerte alle > 0 (definit) bzw. \ge 0 (semidefinit) sind. Definit bedeutet streng konvex und eine eindeutige Lösung, semidefinit konvex mit möglicherweise mehreren. → Abschnitt 11.3
+
+
Präskriptive Analytik — Analysestufe, die vorschreibt, welche Handlung unter den gegebenen Bedingungen die beste ist. Sie folgt auf die deskriptive (was war?) und die prädiktive Stufe (was wird?). → Abschnitt 1.3
+
+
Predict-then-Optimize — Zweistufiges Vorgehen: erst prognostizieren, dann mit der Prognose optimieren. Der Bruch liegt dazwischen — das Prognosemodell minimiert Vorhersagefehler, nicht Entscheidungskosten. → Abschnitt 15.3
+
+
Primales Problem — Das ursprünglich formulierte Optimierungsproblem, im Gegensatz zu seinem Dualproblem. Welches von beiden „primal“ heißt, ist Konvention: Das Duale des Dualen ist wieder das Primale. → Abschnitt 5.6
+
+
pydantic — Bibliothek zur Validierung von Datenstrukturen anhand von Typannotationen. Im Buch prüft sie die Eingaben des Optimierungsdienstes, bevor ein Modell überhaupt gebaut wird. → Abschnitt 3.4
+
+
pymoo — Bibliothek für mehrkriterielle Optimierung mit evolutionären Verfahren. Sie liefert Pareto-Fronten dort, wo sich die Ziele nicht sinnvoll skalarisieren lassen. → Abschnitt 3.4
+
+
Pyomo — Umfangreiche Modellierungssprache für Optimierung in Python, mit Unterstützung für abstrakte Modelle, viele Solver und nichtlineare Formulierungen. Mächtiger, aber ausführlicher als CVXPY. → Abschnitt 3.7
+
Q
+
Q-Learning — Verfahren des bestärkenden Lernens, das den Wert von Zustand-Aktion-Paaren aus Erfahrung schätzt, ohne ein Modell der Umgebung zu kennen. Verwandt mit der dynamischen Programmierung, aber lernend statt rechnend. → Abschnitt 13.5
+
+
Quadratische Programmierung (QP) — Quadratic Programming — Optimierung einer quadratischen Zielfunktion unter linearen Nebenbedingungen. Bei positiv semidefiniter Matrix konvex und damit zuverlässig global lösbar; die Portfoliooptimierung ist der Standardfall. → Abschnitt 11.3
+
R
+
Rebalancing — Periodische Rückführung des Portfolios auf die Zielgewichte. Zu häufig kostet Gebühren, zu selten lässt die Gewichte driften — und die Termine müssen Handelstage sein. → Abschnitt 21.4
+
+
Regime-Shift — Strukturbruch in den Daten, nach dem historisch geschätzte Momente ihre Gültigkeit verlieren. Kein Modellfehler, sondern eine Eigenschaft der Welt — abzufangen über kürzere Schätzfenster und robuste Formulierungen. → Abschnitt 22.3
+
+
Reinforcement Learning — Lernen einer Entscheidungsstrategie aus Rückmeldungen der Umgebung. Es löst dieselbe Klasse mehrstufiger Probleme wie die dynamische Programmierung, benötigt aber kein explizites Übergangsmodell. → Abschnitt 13.5
+
+
Relaxation — Absichtliches Weglassen einschränkender Forderungen — typischerweise der Ganzzahligkeit —, um eine schnell berechenbare Schranke zu gewinnen. Der Wert der Relaxation begrenzt, wie gut die echte Lösung überhaupt werden kann. → Abschnitt 6.4
+
+
Robuste Optimierung — Auslegung auf den ungünstigsten Fall innerhalb einer Unsicherheitsmenge. Sie verlangt keine Wahrscheinlichkeiten, sondern nur eine Angabe darüber, wie weit die Parameter schwanken können. → Abschnitt 12.6
+
+
Rockafellar-Uryasev-Theorem — Ergebnis, das die CVaR-Minimierung in eine konvexe Hilfsfunktion überführt. Es macht die Optimierung des Tail-Risikos mit gewöhnlichen linearen Solvern möglich. → Abschnitt 20.4
+
+
Routing-Bibliothek — Spezialisierter Teil von OR-Tools für Touren- und Reihenfolgeprobleme. Sie arbeitet heuristisch mit Zeitbudget — „optimal“ wird hier in der Regel nicht bewiesen. → Abschnitt 8.10
+
+
Rucksackproblem — Auswahl von Gegenständen mit Wert und Gewicht unter einer Kapazitätsgrenze. Das Schulbeispiel der ganzzahligen Optimierung und Modell für jede Budgetauswahl. → Abschnitt 6.6
+
+
Rückwärtsinduktion — Lösungsrichtung der dynamischen Programmierung: von der letzten Stufe rückwärts. Nur dort steht der Wert eines Zustands ohne Kenntnis der Zukunft fest, und von dort aus lässt sich jede frühere Stufe auf bereits bekannte Werte zurückführen. → Abschnitt 13.2
+
+
Ruiz-Equilibrierung — Skalierungsverfahren, das Zeilen und Spalten einer Matrix iterativ so multipliziert, dass alle Beträge in derselben Größenordnung liegen. Nach wenigen Durchläufen liegen die Maxima bei 1 — die einfachste wirksame Gegenmaßnahme gegen schlechte Konditionierung. → Abschnitt 2.7
+
+
Rüstzeit — Umschaltzeit zwischen zwei aufeinanderfolgenden Aufträgen auf derselben Maschine. Sie ist reihenfolgeabhängig und macht aus einer einfachen Zuordnung ein Reihenfolgeproblem. → Anhang B
+
S
+
SAT (Boolean Satisfiability) — Problem, für eine aussagenlogische Formel eine erfüllende Belegung zu finden. Das erste als NP-vollständig bewiesene Problem — und die Sprache, in die CP-SAT jedes Modell übersetzt. → Abschnitt 7.3
+
+
Satz von Birkhoff und von Neumann — Jede doppelt stochastische Matrix ist eine Mischung von Permutationsmatrizen. Er erklärt, warum das Zuordnungsproblem schon als lineares Programm ganzzahlige Lösungen liefert. → Abschnitt 8.4
+
+
Schattenpreis — Optimalwert einer Dualvariablen: Um wie viel ändert sich der Zielwert, wenn die zugehörige Ressource um eine Einheit erweitert wird? Er gilt nur lokal — innerhalb des Bereichs, den die Sensitivitätsanalyse ausweist. → Abschnitt 5.6
+
+
Schätzfehler — Abweichung geschätzter Größen wie Renditen oder Kovarianzen von ihren wahren Werten. In der Portfoliooptimierung nicht ein Randproblem, sondern das Hauptproblem. → Abschnitt 18.4
+
+
+
Scheduling — Zuweisung von Tätigkeiten an Maschinen oder Personen unter Ressourcen- und Reihenfolgebedingungen. Kapitel 7 behandelt es mit CP-SAT (Intervallvariablen), Kapitel 9 mit Simulated Annealing und LNS. → Kapitel 7
+
+
Schlupfvariable — Nichtnegative Hilfsvariable, die eine Ungleichung in eine Gleichung überführt. Ihr Wert zeigt die ungenutzte Reserve; ist er null, ist die Bedingung bindend. → Abschnitt 5.3
+
+
Schnittebenen (Cutting Planes) — Zusätzliche gültige Ungleichungen, die gebrochene LP-Lösungen abschneiden, ohne einen einzigen ganzzahligen Punkt zu verlieren. Sie verschärfen die Relaxation und verkleinern damit den Suchbaum. → Abschnitt 6.4
+
+
scikit-learn — Bibliothek für maschinelles Lernen. Im Buch für Prognosemodelle und für die Ledoit-Wolf-Shrinkage der Kovarianzmatrix eingesetzt. → Abschnitt 3.4
+
+
SCIP — Akademischer Solver für gemischt-ganzzahlige lineare und nichtlineare Programme, über OR-Tools und Pyomo ansprechbar. Für Forschung und Lehre frei nutzbar; kommerzieller Einsatz verlangt eine eigene Lizenz. → Abschnitt 6.4
+
+
SciPy — Wissenschaftliche Standardbibliothek von Python. Ihr Teilmodul scipy.optimize enthält mit linprog, milp und minimize die Einstiegswerkzeuge für lineare, ganzzahlige und nichtlineare Optimierung. → Abschnitt 3.4
+
+
Semikontinuierliche Variable — Variable, die entweder null ist oder in einem Intervall [m, M] liegt. Sie erfasst Mindestlosgrößen und wird über eine Binärvariable modelliert. → Abschnitt 6.5
+
+
Sensitivitätsanalyse — Untersuchung, in welchem Bereich sich Koeffizienten und Kapazitäten ändern dürfen, ohne die Struktur der Optimallösung zu verändern. Sie macht aus einer Zahl eine Aussage über ihre Belastbarkeit. → Abschnitt 5.8
+
+
Shannon-Entropie — Maß für die Gleichverteiltheit einer Verteilung. Als Zielfunktion eingesetzt erzwingt sie Streuung, ohne eine bestimmte Aufteilung vorzugeben. → Abschnitt 11.5
+
+
Sharpe Ratio — Überrendite über den risikofreien Zins je Einheit Volatilität. Ihre Maximierung ist zunächst nicht konvex, wird es aber durch die Korn-Transformation. → Abschnitt 19.4
+
+
Simplex-Algorithmus — Verfahren von Dantzig, das von Ecke zu Ecke des Polyeders wandert und den Zielfunktionswert dabei monoton verbessert. Im schlechtesten Fall exponentiell, in der Praxis außerordentlich zuverlässig. → Abschnitt 5.4
+
+
Simulated Annealing — Metaheuristik nach dem Vorbild des Metallabkühlens: Verschlechterungen werden anfangs häufig, später kaum noch akzeptiert. So entkommt die Suche lokalen Optima, ohne die Richtung ganz zu verlieren. → Abschnitt 9.5
+
+
Skalarisierung — Zusammenfassung mehrerer Ziele zu einer gewichteten Summe. Bequem, aber mit zwei Haken: Die Gewichte sind Werturteile, und nicht jeder Punkt der Pareto-Front lässt sich so erreichen. → Abschnitt 14.4
+
+
Skalarprodukt — Summe der paarweisen Produkte zweier Vektoren, \mathbf{c}^\top\mathbf{x}. In der Optimierung die kompakte Schreibweise jeder linearen Zielfunktion. → Abschnitt 2.3
+
+
SLSQP — Sequential Least Squares Programming — gradientenbasiertes Verfahren in scipy.optimize.minimize für nichtlineare Probleme mit Nebenbedingungen. Es findet lokale Optima; ohne Konvexität entscheidet der Startpunkt über das Ergebnis. → Abschnitt 11.5
+
+
Snapshot-Prinzip — Betriebsregel, nach der jeder Optimierungslauf auf einem unveränderlichen Datenschnappschuss mit eigener Kennung arbeitet. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar. → Abschnitt 22.5
+
+
+
Solver — Softwarekomponente, die ein mathematisches Modell löst — also das Optimierungsproblem in eine Lösung übersetzt. Kapitel 3 vergleicht die in diesem Buch verwendeten Solver (HiGHS, OR-Tools/CP-SAT, CVXPY) und ihre Stärken. → Kapitel 3
+
+
Solver-Status — Rückgabewert, mit dem ein Solver das Ergebnis einordnet: OPTIMAL (bewiesen optimal), FEASIBLE (zulässig, aber unbewiesen), INFEASIBLE (keine zulässige Lösung), UNBOUNDED (Zielwert unbeschränkt) und UNKNOWN (Abbruch ohne Aussage). Ihn ungeprüft zu übergehen ist die häufigste Fehlerquelle im produktiven Einsatz. → Notation und Abkürzungen
+
+
Starker Dualitätssatz — Aussage, dass primales und duales Problem im Optimum denselben Zielfunktionswert besitzen — bei linearen Programmen immer, sobald beide lösbar sind. Er macht den Dualwert zu einem belastbaren Preis statt zu einer bloßen Schranke. → Abschnitt 5.6
+
+
Stichproben-Kovarianzmatrix — Aus historischen Daten geschätzte Kovarianzmatrix. Bei weniger Beobachtungen als Titeln ist sie singulär und damit nicht invertierbar; schon bei ähnlicher Größenordnung wird sie extrem instabil. → Abschnitt 18.4
+
+
Stochastische Programmierung — Optimierung unter Unsicherheit mit bekannten Wahrscheinlichkeiten. In der zweistufigen Form wird zwischen Entscheidungen vor und nach der Beobachtung unterschieden — genau der Unterschied, den der Fluch des Durchschnitts einebnet. → Abschnitt 12.5
+
+
Strafkosten — Kosten, mit denen die Verletzung einer weichen Bedingung in der Zielfunktion belegt wird. Ihre Höhe ist eine Modellierungsentscheidung: Sie legt fest, wie viel Regelverstoß was wert ist. → Abschnitt 1.6
+
+
Subadditivität — Eigenschaft \rho(A+B) \le \rho(A)+\rho(B): Diversifikation darf das Risiko nicht erhöhen. Der VaR verletzt sie, weshalb er kein kohärentes Risikomaß ist. → Abschnitt 20.4
+
+
Subtour — Kurzzyklus, der nur einen Teil der Knoten enthält und das Depot umgeht. Ohne eigene Bedingungen — etwa MTZ — liefert ein Tourenmodell solche Gebilde als scheinbar optimale Lösung. → Abschnitt 8.5
+
+
Survivorship-Bias — Verzerrung durch die Auswahl nur derjenigen Titel, die es bis heute gibt. Die ausgeschiedenen fehlen — und mit ihnen die schlechtesten Verläufe. → Abschnitt 21.6
+
+
Symmetriebrechung — Zusätzliche Ordnungsbedingungen, die verhindern, dass der Solver gleichwertige Vertauschungen mehrfach durchsucht. Bei identischen Maschinen oder Fahrzeugen oft der größte einzelne Laufzeitgewinn. → Anhang B
+
T
+
Tabu-Suche — Metaheuristik, die zuletzt besuchte Lösungen für einige Schritte sperrt. Die Sperrliste zwingt die Suche aus lokalen Optima heraus, statt zwischen zwei Nachbarn zu pendeln. → Abschnitt 9.8
+
+
Tangentialportfolio — Portfolio auf der Effizienzgrenze mit der höchsten Sharpe Ratio — der Berührpunkt der Kapitalmarktlinie. Es reagiert besonders empfindlich auf Renditeschätzfehler. → Abschnitt 19.4
+
+
Technologiematrix — Die Matrix \mathbf{A} der Verbrauchskoeffizienten. Ihr Eintrag a_{ij} sagt, wie viel von Ressource i eine Einheit von Aktivität j verbraucht. Zusammen mit dem Kapazitätsvektor \mathbf{b} bildet sie die Nebenbedingungen \mathbf{A}\mathbf{x} \le \mathbf{b}. → Abschnitt 2.3
+
+
Totale Unimodularität — Eigenschaft einer Matrix, bei der jede quadratische Teilmatrix die Determinante 0, +1 oder -1 hat. Folge: Alle Ecken des Polyeders sind ganzzahlig — Ganzzahligkeit muss gar nicht gefordert werden. → Abschnitt 8.4
+
+
Transaktionskosten — Beim Umschichten anfallende Kosten aus Gebühren, Spread und Marktauswirkung. Über eine L_1-Strafe modellierbar, die zugleich den Turnover begrenzt. → Abschnitt 20.5
+
+
Trickle Flow — Fehlerbild bei zu großem M: Ein verschwindend kleiner Schaltwert — etwa 4 \cdot 10^{-8} — gilt dem Solver als null, lässt aber multipliziert mit einem riesigen M einen großen Fluss durch. Das Lager liefert, ohne offiziell geöffnet zu sein, und die Fixkosten fehlen in der Rechnung. → Abschnitt A.6
+
+
TSP (Traveling Salesperson Problem) — Suche nach der kürzesten Rundreise durch n Orte. Das bekannteste NP-schwere Problem und der Ausgangspunkt aller Tourenmodelle. → Abschnitt 8.5
+
+
Turnover (Umschlag) — Summe der absoluten Gewichtsänderungen einer Umschichtung. Die Größe, an der Transaktionskosten hängen — und die Kennzahl, mit der man Rebalancing-Regeln vergleicht. → Abschnitt 20.5
+
+
TWAP — Time-Weighted Average Price — Ausführungsstrategie, die eine Order gleichmäßig über die Zeit verteilt. Einfache Vergleichsgröße für jede optimierte Ausführung. → Abschnitt 13.4
+
U
+
Umrüstkosten — Kosten, die beim Wechsel zwischen zwei Produkten anfallen. Sie hängen vom Vorgänger ab und erzwingen deshalb Binärvariablen über Paare statt über einzelne Aufträge. → Anhang B
+
+
Unbounded — Zustand, in dem der Zielwert unbeschränkt verbessert werden kann. Fast immer ein Modellfehler — eine fehlende Kapazitätsgrenze oder ein vergessenes Vorzeichen —, nicht ein besonders gutes Ergebnis. → Anhang C
+
+
Ungarischer Algorithmus — Kombinatorisches Verfahren für das Zuordnungsproblem mit polynomialer Laufzeit. Es löst exakt, was als allgemeines MILP deutlich teurer wäre. → Abschnitt 8.10
+
+
Unsicherheitsmenge — Vorab definierter Bereich möglicher Parameterwerte, gegen dessen ungünstigstes Element eine robuste Lösung abgesichert wird. Ihre Größe steuert unmittelbar den Preis der Robustheit. → Abschnitt 12.6
+
+
Unsinniges Ergebnis — Fehlerbild, bei dem der Solver zwar OPTIMAL meldet, die Lösung aber offensichtlich unbrauchbar ist. Dann ist nicht die Rechnung falsch, sondern das Modell: Es beschreibt eine andere Wirklichkeit als die gemeinte. → Anhang C
+
V
+
Value at Risk (VaR) — Verlustschwelle, die mit vorgegebener Wahrscheinlichkeit nicht überschritten wird. Sie sagt nichts über die Verlusthöhe dahinter und ist nicht subadditiv — beides Gründe für den Übergang zum CVaR. → Abschnitt 20.4
+
+
Vehicle Routing Problem (VRP) — Verallgemeinerung des TSP auf mehrere Fahrzeuge mit Depot und Kapazitätsgrenzen. In der Variante mit Zeitfenstern (VRPTW) ist es der praktische Standardfall der Tourenplanung. Sich kreuzende Touren sind hier kein Fehler: Die Zeitfenster können die Reihenfolge erzwingen. → Abschnitt 8.5
+
+
Verdächtig guter Backtest — Fehlerbild der Finanzkapitel: eine Strategie, die im Rückblick zu gut abschneidet. Die Prüfreihenfolge lautet Lookahead, Survivorship, Kosten, Data Snooping — in dieser Reihenfolge, weil sie nach Häufigkeit sortiert ist. → Anhang C
+
+
Verhältnis-Bedingung — Anforderung der Art „höchstens 30 % aus einer Quelle“. Sie wird linear, sobald man sie als x_i \le 0{,}3 \sum_j x_j schreibt statt als Bruch. → Anhang B
+
+
Vertauschte Spalten — Fehlerbild, bei dem Daten und Modell unterschiedliche Reihenfolgen annehmen. Es rechnet widerspruchsfrei und liefert dennoch Unsinn — deshalb gehört die Spaltenreihenfolge abgesichert, nicht angenommen. → Anhang C
+
+
Volatilität — Standardabweichung der Renditen, üblicherweise auf ein Jahr skaliert. Sie misst Schwankung in beide Richtungen und behandelt Gewinne damit wie Verluste. → Abschnitt 18.3
+
+
Vorrangbeziehung — Bedingung „Aktivität A muss vor Aktivität B abgeschlossen sein“, formuliert als \text{Ende}_A \le \text{Start}_B. Der Grundbaustein jeder Ablaufplanung. → Anhang B
+
W
+
Walk-Forward-Backtest — Rollierende Auswertung, bei der Parameter stets nur auf Vergangenheitsdaten geschätzt und auf dem unmittelbar folgenden Zeitraum getestet werden. Die einzige Backtest-Bauform, die Lookahead strukturell ausschließt. → Abschnitt 21.5
+
+
Warm-Start — Übergabe einer bekannten Lösung als Startpunkt. Bei wiederholten, ähnlichen Läufen — etwa täglicher Neuplanung — oft der größte Zeitgewinn überhaupt. → Abschnitt 6.8
+
+
Weiche Grenze mit Strafkosten — Modellierungsmuster, das eine Schranke überschreitbar macht und die Überschreitung über eine Schlupfvariable in der Zielfunktion bepreist. So bleibt das Modell lösbar und zeigt trotzdem, wo es klemmt. → Anhang B
+
+
Widersprüchliche Solver — Fehlerbild, bei dem zwei Solver verschiedene Lösungen melden. Bei gleichem Zielwert sind es Alternativoptima und beide haben recht; bei verschiedenem Zielwert liegt ein Modell- oder Toleranzproblem vor. → Anhang C
+
+
Wurzel-Zeit-Regel — Skalierung der Volatilität mit \sqrt{T}. Sie gilt streng nur für Standardabweichungen unabhängiger Größen ohne Drift — für VaR und CVaR ist sie lediglich eine grobe Näherung. → Abschnitt 20.6
+
Y
+
yfinance — Bibliothek zum Abruf historischer Kursdaten. Die einzige Abhängigkeit des Buchs, die eine Internetverbindung braucht — vier Programme nutzen sie. → Abschnitt 3.4
+
Z
+
Zielfunktion — Der zu minimierende oder maximierende Ausdruck, der die Entscheidungsvariablen zu einer einzigen Bewertungszahl verdichtet. Ihre Formulierung entscheidet, was das Modell überhaupt für gut hält. → Abschnitt 1.6
+
+
Zufallsmatrizentheorie — Random Matrix Theory — Theorie der Eigenwertverteilung zufälliger Matrizen. Sie liefert den Maßstab dafür, welche Struktur in einer geschätzten Kovarianzmatrix echt ist und welche bloßes Rauschen. → Abschnitt 18.4
+
+
Zulässiger Bereich (Feasible Region) — Menge aller Punkte, die sämtliche Nebenbedingungen gleichzeitig erfüllen. Ist sie leer, ist das Modell unlösbar; ist sie unbeschränkt, kann der Zielwert entgleiten. → Anhang F
+
+
Zuordnungsproblem — Eins-zu-eins-Zuordnung zweier gleich großer Mengen mit minimalen Kosten. Dank totaler Unimodularität schon als LP ganzzahlig lösbar. → Anhang B
+
+
Zustandslosigkeit — Betriebsprinzip, nach dem der rechnende Dienst keinen Zustand hält: Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial. → Abschnitt 22.5
+
+
Zustandsraum — Menge aller Zustände, die ein mehrstufiges Entscheidungsproblem annehmen kann. Seine Größe entscheidet, ob dynamische Programmierung praktikabel ist. → Abschnitt 13.5
+
+
Zwei-Phasen-Simplex-Methode — Vorgehen, um überhaupt eine erste zulässige Ecke zu finden. Phase 1 minimiert mit Hilfsvariablen die Verletzung der Nebenbedingungen; bleibt dabei ein Rest, ist das Modell unlösbar. Erst Phase 2 optimiert die eigentliche Zielfunktion. → Abschnitt A.5
+
+
Zweischichtige Architektur — Trennung in eine Modellierungsschicht, in der man das Problem mathematiknah formuliert, und eine Solver-Schicht aus hochoptimierten C++-Bibliotheken. Man schreibt Optimierungsalgorithmen im modernen OR nicht selbst. → Abschnitt 3.3
+
Anhang F: Literaturverzeichnis
Die Blöcke folgen der Reihenfolge der Kapitel. Jeder Eintrag trägt eine Relevanzangabe und einen Themen-Tag:
@@ -33176,83 +33659,83 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Grundlagen und Standardwerke des Operations Research
★★★ Bertsimas, D. & Tsitsiklis, J. N. (1997):Introduction to Linear Optimization. Athena Scientific, Belmont. [Lineare Optimierung] Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, Sensitivitätsanalyse und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der Dualitätstheorie.
-
★★★ Nocedal, J. & Wright, S. J. (2006):Numerical Optimization (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und Ruiz-Equilibrierung.
+
★★★ Bertsimas, D. & Tsitsiklis, J. N. (1997):Introduction to Linear Optimization. Athena Scientific, Belmont. [Lineare Optimierung] Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, Sensitivitätsanalyse und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der Dualitätstheorie.
+
★★★ Nocedal, J. & Wright, S. J. (2006):Numerical Optimization (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und Ruiz-Equilibrierung.
★★ Hillier, F. S. & Lieberman, G. J. (2021):Introduction to Operations Research (11. Aufl.). McGraw-Hill, New York. [OR-Überblick] Breite Einführung inklusive Warteschlangen, Entscheidungsmodellen und Simulation — die Themen, die dieses Buch bewusst auslässt.
★★ Kallrath, J. (2021):Business Optimization Using Mathematical Programming (2. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-73237-0 [Modellierungspraxis] Der Brückenschlag von der betrieblichen Fragestellung zum Modell, mit ausführlichem Katalog industrieller Anwendungsfälle. Nächster Nachbar zu Kapitel 4.
-
★ Dantzig, G. B. (1963):Linear Programming and Extensions. Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] Das Originalwerk des Erfinders des Simplex-Algorithmus. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.
+
★ Dantzig, G. B. (1963):Linear Programming and Extensions. Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] Das Originalwerk des Erfinders des Simplex-Algorithmus. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.
Modellierung in Python: Bibliotheken und Modellierungsschichten
★★★ Diamond, S. & Boyd, S. (2016):CVXPY: A Python-embedded modeling language for convex optimization. In: Journal of Machine Learning Research 17(83), S. 1–5. [Bibliothek: CVXPY] Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. www.cvxpy.org
+
★★★ Diamond, S. & Boyd, S. (2016):CVXPY: A Python-embedded modeling language for convex optimization. In: Journal of Machine Learning Research 17(83), S. 1–5. [Bibliothek: CVXPY] Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. www.cvxpy.org
★★ Agrawal, A., Verschueren, R., Diamond, S. & Boyd, S. (2018):A rewriting system for convex optimization problems. In: Journal of Control and Decision 5(1), S. 42–60. DOI: 10.1080/23307706.2017.1397554 [Bibliothek: CVXPY] Erklärt, was CVXPY beim Übersetzen tatsächlich tut — und warum ein DCPError kein Solverfehler ist.
-
★★★ Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):Pyomo — Optimization Modeling in Python (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.
-
★★ Hofmann, F. (2023):Linopy: Linear optimization with n-dimensional labeled variables. In: Journal of Open Source Software 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.
-
★★ Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):SciPy 1.0: fundamental algorithms for scientific computing in Python. In: Nature Methods 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] Die zitierfähige Quelle zu scipy.optimize — dem Einstiegspunkt in Kapitel 5.
-
★★ Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):Array programming with NumPy. In: Nature 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.
+
★★★ Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):Pyomo — Optimization Modeling in Python (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.
+
★★ Hofmann, F. (2023):Linopy: Linear optimization with n-dimensional labeled variables. In: Journal of Open Source Software 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.
+
★★ Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):SciPy 1.0: fundamental algorithms for scientific computing in Python. In: Nature Methods 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] Die zitierfähige Quelle zu scipy.optimize — dem Einstiegspunkt in Kapitel 5.
+
★★ Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):Array programming with NumPy. In: Nature 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.
★★★ Wolsey, L. A. (2020):Integer Programming (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] Branch-and-Bound, Branch-and-Cut, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines M besser ist als ein großes.
+
★★★ Wolsey, L. A. (2020):Integer Programming (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] Branch-and-Bound, Branch-and-Cut, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines M besser ist als ein großes.
★★★ Conforti, M., Cornuéjols, G. & Zambelli, G. (2014):Integer Programming. Springer, Cham. DOI: 10.1007/978-3-319-11008-0 [Ganzzahlige Optimierung] Modernere Darstellung als Wolsey, mit ausführlicher Behandlung von Schnittebenen und totaler Unimodularität.
★★ Klotz, E. & Newman, A. M. (2013):Practical guidelines for solving difficult mixed integer linear programs. In: Surveys in Operations Research and Management Science 18(1–2), S. 18–32. DOI: 10.1016/j.sorms.2012.12.001 [Ganzzahlige Optimierung] Was zu tun ist, wenn ein Modell nicht fertig rechnet — Skalierung, Formulierung, Symmetrie. Die Literaturgrundlage von Anhang C.
-
★★ Achterberg, T. (2009):SCIP: solving constraint integer programs. In: Mathematical Programming Computation 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [Solver: SCIP] Beschreibt die Verbindung von MILP und Constraint Programming in einem Solver.
+
★★ Achterberg, T. (2009):SCIP: solving constraint integer programs. In: Mathematical Programming Computation 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [Solver: SCIP] Beschreibt die Verbindung von MILP und Constraint Programming in einem Solver.
Moderne Solver: Implementierung, Vergleich und Benchmarks
Zu Kapitel 3 und Anhang D. Neuer Abschnitt: Das Buch vergleicht durchgehend Solver, die Literatur dazu stand bisher verstreut.
-
★★★ Huangfu, Q. & Hall, J. A. J. (2018):Parallelizing the dual revised simplex method. In: Mathematical Programming Computation 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: HiGHS] Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von scipy.optimize.linprog und CVXPY. highs.dev
+
★★★ Huangfu, Q. & Hall, J. A. J. (2018):Parallelizing the dual revised simplex method. In: Mathematical Programming Computation 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: HiGHS] Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von scipy.optimize.linprog und CVXPY. highs.dev
★★★ Gleixner, A., Hendel, G., Gamrath, G. et al. (2021):MIPLIB 2017: data-driven compilation of the 6th mixed-integer programming library. In: Mathematical Programming Computation 13(3), S. 443–490. DOI: 10.1007/s12532-020-00194-3 [Benchmarks] Die Testmenge, auf der praktisch jeder Solververgleich beruht: 1065 Instanzen, davon 240 als Benchmark-Teilmenge. Frei zugänglich; Daten unter miplib.zib.de.
-
★★★ Mittelmann, H. D.:Benchmarks for Optimization Software. Arizona State University. plato.asu.edu/bench.html [Benchmarks] Die laufend aktualisierte Referenzmessung für LP-, MILP- und QP-Solver. Wichtige Einschränkung: Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich Gurobi gegen HiGHS ist dort nicht mehr möglich.
+
★★★ Mittelmann, H. D.:Benchmarks for Optimization Software. Arizona State University. plato.asu.edu/bench.html [Benchmarks] Die laufend aktualisierte Referenzmessung für LP-, MILP- und QP-Solver. Wichtige Einschränkung: Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich Gurobi gegen HiGHS ist dort nicht mehr möglich.
★★ Bolusani, S., Besançon, M., Bestuzheva, K. et al. (2024):The SCIP Optimization Suite 9.0. Technischer Bericht, Zuse-Institut Berlin. arXiv:2402.17702 [Solver: SCIP] Beschreibt Aufbau und Komponenten der Suite. Die Reihe wird fortgeschrieben — die jeweils aktuelle Fassung steht unter scipopt.org; beim Zitieren die tatsächlich benutzte Version angeben.
★★★ Rossi, F., van Beek, P. & Walsh, T. (Hrsg., 2006):Handbook of Constraint Programming. Foundations of Artificial Intelligence, Bd. 2. Elsevier, Amsterdam. [Constraint Programming] Nach wie vor das Referenzwerk zu Propagation, globalen Constraints und Suchstrategien.
-
★★★ Perron, L. & Furnon, V.:OR-Tools CP-SAT Solver Documentation. Google. developers.google.com/optimization/cp [Solver: CP-SAT] Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu AddNoOverlap und AddCumulative deckt sich mit Kapitel 7.
-
★★ Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):IBM ILOG CP Optimizer for scheduling. In: Constraints 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [Scheduling] Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.
+
★★★ Perron, L. & Furnon, V.:OR-Tools CP-SAT Solver Documentation. Google. developers.google.com/optimization/cp [Solver: CP-SAT] Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu AddNoOverlap und AddCumulative deckt sich mit Kapitel 7.
+
★★ Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):IBM ILOG CP Optimizer for scheduling. In: Constraints 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [Scheduling] Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.
★★ Biere, A., Heule, M., van Maaren, H. & Walsh, T. (Hrsg., 2021):Handbook of Satisfiability (2. Aufl.). Frontiers in Artificial Intelligence and Applications, Bd. 336. IOS Press, Amsterdam. DOI: 10.3233/FAIA336 [SAT] Die Grundlage dessen, was CP-SAT unter der Haube tut: Conflict-Driven Clause Learning, Klausellernen, Neustarts.
★★★ Ahuja, R. K., Magnanti, T. L. & Orlin, J. B. (1993):Network Flows: Theory, Algorithms, and Applications. Prentice Hall, Englewood Cliffs. [Netzwerkflüsse] Das Standardwerk zu Min-Cost-Flow, Matching und totaler Unimodularität — der Grund, warum diese Modelle schon als LP ganzzahlig lösen.
-
★★★ Toth, P. & Vigo, D. (Hrsg., 2014):Vehicle Routing: Problems, Methods, and Applications (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [VRP] Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und Subtour-Eliminierung.
+
★★★ Toth, P. & Vigo, D. (Hrsg., 2014):Vehicle Routing: Problems, Methods, and Applications (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [VRP] Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und Subtour-Eliminierung.
★★ Vidal, T., Laporte, G. & Matl, P. (2020):A concise guide to existing and emerging vehicle routing problem variants. In: European Journal of Operational Research 286(2), S. 401–416. DOI: 10.1016/j.ejor.2019.10.010 [VRP] Übersicht über die Variantenvielfalt — hilfreich, um die eigene Aufgabe einzuordnen, bevor man modelliert. Frei zugänglich als arXiv:1906.06750.
-
★ Kuhn, H. W. (1955):The Hungarian method for the assignment problem. In: Naval Research Logistics Quarterly 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [Zuordnungsproblem] Das Originalverfahren hinter scipy.optimize.linear_sum_assignment.
+
★ Kuhn, H. W. (1955):The Hungarian method for the assignment problem. In: Naval Research Logistics Quarterly 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [Zuordnungsproblem] Das Originalverfahren hinter scipy.optimize.linear_sum_assignment.
★★★ Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):Handbook of Metaheuristics (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 MetaheuristikenSammelband zu Tabu-Suche, Simulated Annealing, LNS, genetischen Verfahren — jeweils von den Urhebern geschrieben.
+
★★★ Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):Handbook of Metaheuristics (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 MetaheuristikenSammelband zu Tabu-Suche, Simulated Annealing, LNS, genetischen Verfahren — jeweils von den Urhebern geschrieben.
★★★ Pisinger, D. & Ropke, S. (2010):Large Neighborhood Search. In: Gendreau, M. & Potvin, J.-Y. (Hrsg.): Handbook of Metaheuristics, S. 399–419. Springer. DOI: 10.1007/978-1-4419-1665-5_13 MetaheuristikenDie Darstellung des Verfahrens, das im Buch die exakte Suche auf Teilproblemen mit heuristischem Zerstören verbindet.
★★ Kirkpatrick, S., Gelatt, C. D. & Vecchi, M. P. (1983):Optimization by Simulated Annealing. In: Science 220(4598), S. 671–680. DOI: 10.1126/science.220.4598.671 MetaheuristikenDie Originalarbeit; die Abkühlungsanalogie stammt von hier.
-
★★ Sörensen, K. (2015):Metaheuristics — the metaphor exposed. In: International Transactions in Operational Research 22(1), S. 3–18. DOI: 10.1111/itor.12001 MetaheuristikenKritik an der Flut naturanaloger „neuer” Verfahren. Vor der Auswahl einer Metaheuristik zu lesen — sie erspart viel Zeit.
+
★★ Sörensen, K. (2015):Metaheuristics — the metaphor exposed. In: International Transactions in Operational Research 22(1), S. 3–18. DOI: 10.1111/itor.12001 MetaheuristikenKritik an der Flut naturanaloger „neuer” Verfahren. Vor der Auswahl einer Metaheuristik zu lesen — sie erspart viel Zeit.
★★★ Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):Column Generation. Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.
+
★★★ Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):Column Generation. Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.
★★★ Lübbecke, M. E. & Desrosiers, J. (2005):Selected Topics in Column Generation. In: Operations Research 53(6), S. 1007–1023. DOI: 10.1287/opre.1050.0234 [Spaltengenerierung] Kompakter als das Buch und für den Einstieg besser geeignet; erklärt das Preisproblem sauber.
★★ Rahmaniani, R., Crainic, T. G., Gendreau, M. & Rei, W. (2017):The Benders decomposition algorithm: A literature review. In: European Journal of Operational Research 259(3), S. 801–817. DOI: 10.1016/j.ejor.2016.12.005 [Dekomposition] Übersicht über Varianten und Beschleunigungen — die Gegenrichtung zur Spaltengenerierung.
★★★ Boyd, S. & Vandenberghe, L. (2004):Convex Optimization. Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] Referenzwerk für konvexe Mengen, KKT-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter stanford.edu/~boyd/cvxbook.
-
★★ Kraft, D. (1988):A Software Package for Sequential Quadratic Programming. DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] Die Originalarbeit zu SLSQP — dem Verfahren hinter scipy.optimize.minimize.
-
★★ Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):Applications of second-order cone programming. In: Linear Algebra and its Applications 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [Konische Optimierung] Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in Kapitel 12.
+
★★★ Boyd, S. & Vandenberghe, L. (2004):Convex Optimization. Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] Referenzwerk für konvexe Mengen, KKT-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter stanford.edu/~boyd/cvxbook.
+
★★ Kraft, D. (1988):A Software Package for Sequential Quadratic Programming. DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] Die Originalarbeit zu SLSQP — dem Verfahren hinter scipy.optimize.minimize.
+
★★ Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):Applications of second-order cone programming. In: Linear Algebra and its Applications 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [Konische Optimierung] Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in Kapitel 12.
Optimierung unter Unsicherheit: stochastisch, robust, Chance Constraints
Zu Kapitel 12 und Kapitel 17. Aus dem früheren Block „Konvexe, nichtlineare und robuste Optimierung” herausgelöst und erweitert.
-
★★★ Birge, J. R. & Louveaux, F. (2011):Introduction to Stochastic Programming (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information (EVPI).
-
★★★ Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):Robust Optimization. Princeton University Press. DOI: 10.1515/9781400831050 [Robuste Optimierung] Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.
-
★★★ Bertsimas, D. & Sim, M. (2004):The Price of Robustness. In: Operations Research 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] Quelle des Budgeted-Uncertainty-Ansatzes: Der Parameter \Gamma regelt stufenlos, wie vorsichtig die Lösung wird.
+
★★★ Birge, J. R. & Louveaux, F. (2011):Introduction to Stochastic Programming (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information (EVPI).
+
★★★ Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):Robust Optimization. Princeton University Press. DOI: 10.1515/9781400831050 [Robuste Optimierung] Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.
+
★★★ Bertsimas, D. & Sim, M. (2004):The Price of Robustness. In: Operations Research 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] Quelle des Budgeted-Uncertainty-Ansatzes: Der Parameter\Gamma regelt stufenlos, wie vorsichtig die Lösung wird.
★★ Nemirovski, A. & Shapiro, A. (2007):Convex Approximations of Chance Constrained Programs. In: SIAM Journal on Optimization 17(4), S. 969–996. DOI: 10.1137/050622328 [Chance Constraints] Zeigt, wann sich eine Wahrscheinlichkeitsbedingung konvex annähern lässt — und wann nicht.
★★ Charnes, A. & Cooper, W. W. (1959):Chance-Constrained Programming. In: Management Science 6(1), S. 73–79. DOI: 10.1287/mnsc.6.1.73 [Chance Constraints] Die Originalarbeit, die den Begriff geprägt hat.
★★ Savage, S. L. (2009):The Flaw of Averages. Wiley, Hoboken. [Unsicherheit] Die populäre Darstellung des Fluchs des Durchschnitts. Ohne Formeln, dafür mit den Beispielen, die in Besprechungen wirken.
@@ -33260,17 +33743,17 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Dynamische Programmierung und sequenzielle Entscheidungen
★★★ Powell, W. B. (2022):Reinforcement Learning and Stochastic Optimization: A Unified Framework for Sequential Decisions. Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die Kapitel 13 am Schluss vornimmt.
-
★★★ Bertsekas, D. P. (2017):Dynamic Programming and Optimal Control, Vol. I (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] Die mathematisch strenge Darstellung des Optimalitätsprinzips und der Rückwärtsinduktion.
+
★★★ Powell, W. B. (2022):Reinforcement Learning and Stochastic Optimization: A Unified Framework for Sequential Decisions. Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die Kapitel 13 am Schluss vornimmt.
+
★★★ Bertsekas, D. P. (2017):Dynamic Programming and Optimal Control, Vol. I (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] Die mathematisch strenge Darstellung des Optimalitätsprinzips und der Rückwärtsinduktion.
★★ Sutton, R. S. & Barto, A. G. (2018):Reinforcement Learning: An Introduction (2. Aufl.). MIT Press, Cambridge. [Reinforcement Learning] Der Standardtext zum lernenden Gegenstück. Frei verfügbar auf der Seite der Autoren.
★ Bellman, R. (1957):Dynamic Programming. Princeton University Press. [Historisch] Das Originalwerk. Für die praktische Arbeit heute durch Powell und Bertsekas ersetzt.
★★★ Ehrgott, M. (2005):Multicriteria Optimization (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 Mehrkriterielle OptimierungPareto-Optimalität, Skalarisierung und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.
+
★★★ Ehrgott, M. (2005):Multicriteria Optimization (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 Mehrkriterielle OptimierungPareto-Optimalität, Skalarisierung und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.
★★ Miettinen, K. (1999):Nonlinear Multiobjective Optimization. Kluwer, Boston. DOI: 10.1007/978-1-4615-5563-6 Mehrkriterielle OptimierungSystematik der Verfahren, geordnet danach, wann die Präferenz des Entscheiders einfließt.
-
★★ Blank, J. & Deb, K. (2020):pymoo: Multi-Objective Optimization in Python. In: IEEE Access 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. pymoo.org
+
★★ Blank, J. & Deb, K. (2020):pymoo: Multi-Objective Optimization in Python. In: IEEE Access 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. pymoo.org
Lernbasierte Optimierung: maschinelles Lernen und OR
Zu Kapitel 15. Neuer Abschnitt — im Buch als eigenes Kapitel behandelt, in der Literaturliste bisher gar nicht vertreten.
@@ -33292,21 +33775,21 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
★★★ Markowitz, H. (1952):Portfolio Selection. In: The Journal of Finance 7(1), S. 77–91. DOI: 10.2307/2975974 [Portfoliotheorie] Die Arbeit, die das Zusammenspiel von Rendite und Kovarianz zum Optimierungsproblem gemacht hat.
-
★★★ Rockafellar, R. T. & Uryasev, S. (2000):Optimization of Conditional Value-at-Risk. In: Journal of Risk 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] Der Satz, der die CVaR-Minimierung in ein lineares Programm überführt.
+
★★★ Rockafellar, R. T. & Uryasev, S. (2000):Optimization of Conditional Value-at-Risk. In: Journal of Risk 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] Der Satz, der die CVaR-Minimierung in ein lineares Programm überführt.
★★★ Ledoit, O. & Wolf, M. (2004):A well-conditioned estimator for large-dimensional covariance matrices. In: Journal of Multivariate Analysis 88(2), S. 365–411. DOI: 10.1016/S0047-259X(03)00096-4 [Kovarianzschätzung] Quelle des von scikit-learn verwendeten Shrinkage-Ziels (skalierte Einheitsmatrix).
★★★ Cornuéjols, G., Peña, J. & Tütüncü, R. (2018):Optimization Methods in Finance (2. Aufl.). Cambridge University Press. DOI: 10.1017/9781107297340 [Finanzoptimierung] Die Brücke zwischen Optimierungstheorie und Finanzanwendung; deckt QP, SOCP und robuste Portfolios ab.
★★★ Almgren, R. & Chriss, N. (2001):Optimal Execution of Portfolio Transactions. In: Journal of Risk 3(2), S. 5–39. DOI: 10.21314/jor.2001.041 [Orderausführung] Das Modell hinter dem Beispiel zur optimalen Orderausführung in Kapitel 13.
★★ Ledoit, O. & Wolf, M. (2003):Improved estimation of the covariance matrix of stock returns with an application to portfolio selection. In: Journal of Empirical Finance 10(5), S. 603–621. DOI: 10.1016/S0927-5398(03)00007-0 [Kovarianzschätzung] Die frühere Arbeit mit dem Konstant-Korrelations-Ziel.
★★ DeMiguel, V., Garlappi, L. & Uppal, R. (2009):Optimal Versus Naive Diversification: How Inefficient is the 1/N Portfolio Strategy? In: Review of Financial Studies 22(5), S. 1915–1953. DOI: 10.1093/rfs/hhm075 [Portfoliotheorie] Die Studie, die 1/N als ernstzunehmenden Vergleichsmaßstab etabliert hat — der Grund, warum im Buch jeder Optimierer gegen sie antreten muss.
-
★★ López de Prado, M. (2018):Advances in Financial Machine Learning. Wiley, Hoboken. [Backtesting] Backtest-Overfitting, Deflated Sharpe Ratio, Denoising der Kovarianzmatrix. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.
+
★★ López de Prado, M. (2018):Advances in Financial Machine Learning. Wiley, Hoboken. [Backtesting] Backtest-Overfitting, Deflated Sharpe Ratio, Denoising der Kovarianzmatrix. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.
★★ Bailey, D. H. & López de Prado, M. (2014):The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality. In: The Journal of Portfolio Management 40(5), S. 94–107. DOI: 10.3905/jpm.2014.40.5.094 [Backtesting] Beziffert, wie stark eine Sharpe Ratio zu korrigieren ist, wenn man viele Strategien ausprobiert hat.
Praxis: Betrieb, Test und Auslieferung von Optimierungsmodellen
★★★ Chinneck, J. W. (2008):Feasibility and Infeasibility in Optimization: Algorithms and Computational Methods. Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen (IIS). Direkte Grundlage von Anhang C.
+
★★★ Chinneck, J. W. (2008):Feasibility and Infeasibility in Optimization: Algorithms and Computational Methods. Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen (IIS). Direkte Grundlage von Anhang C.
★★ Sculley, D., Holt, G., Golovin, D. et al. (2015):Hidden Technical Debt in Machine Learning Systems. In: Advances in Neural Information Processing Systems 28, S. 2503–2511. [Betrieb] Über Systeme, deren Modell nur ein kleiner Teil ist — überträgt sich unmittelbar auf den Betrieb von Optimierungsdiensten. Frei zugänglich.
-
★★ Kleppmann, M. (2017):Designing Data-Intensive Applications. O’Reilly, Sebastopol. [Betrieb] Snapshot-Prinzip, Zustandslosigkeit, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in Kapitel 22.
+
★★ Kleppmann, M. (2017):Designing Data-Intensive Applications. O’Reilly, Sebastopol. [Betrieb] Snapshot-Prinzip, Zustandslosigkeit, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in Kapitel 22.
★★ Ramírez, S.:FastAPI Documentation.fastapi.tiangolo.com [Betrieb] Die Grundlage des Optimierungsdienstes in Kapitel 23, einschließlich der Eingabevalidierung über pydantic.
Software, Dokumentation und Datenquellen
@@ -33318,7 +33801,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
@@ -33327,7 +33810,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
INFORMS — Institute for Operations Research and the Management Sciences. www.informs.org
GOR — Gesellschaft für Operations Research e. V., deutschsprachiger Fachverband. www.gor-ev.de
EURO — Association of European Operational Research Societies. www.euro-online.org
-
Basel Committee on Banking Supervision:Minimum capital requirements for market risk (Basel III, FRTB). Bank for International Settlements. Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf Expected Shortfall.
+
Basel Committee on Banking Supervision:Minimum capital requirements for market risk (Basel III, FRTB). Bank for International Settlements. Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf Expected Shortfall.
Europäische Union:MiFID II (Richtlinie 2014/65/EU) sowie die ESMA-Leitlinien zum algorithmischen Handel. Regulatorischer Rahmen für die Handelsmaschine aus Kapitel 21.
Änderungsprotokoll
@@ -33335,7 +33818,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
Entfernt: kein einziger Eintrag. Alle 27 Einträge der Vorgängerfassung wurden geprüft; sie sind korrekt zitiert, in ihrem Feld weiterhin maßgeblich und nicht redundant zueinander. Es gab damit keine Streichkandidaten. Zwei Einträge sind jedoch auf ★ optional herabgestuft, weil sie historisch bedeutend, für die tägliche Arbeit aber entbehrlich sind: Dantzig (1963) und Bellman (1957) — beide inzwischen durch modernere Darstellungen ersetzt, die im selben Block stehen. Hillier & Lieberman (2021) bleibt bei ★★, ist aber ausdrücklich als Überblickswerk ohne Python-Bezug gekennzeichnet.
Neu aufgenommen: 60 Einträge — von 27 auf 87. Davon sind 72 mit einer Relevanzangabe versehen (34 × ★★★, 35 × ★★, 3 × ★); die übrigen 15 sind Softwareverweise, Datenquellen und Institutionen ohne Autorenschaft. 63 Einträge tragen eine Jahresangabe, 22 davon stammen aus 2018 oder später.
Zur Vorgabe „bevorzugt Werke ab 2018”: Sie ließ sich nur teilweise einlösen, und zwar aus der Sache heraus. In weiten Teilen des Operations Research sind die maßgeblichen Werke älter — Ahuja/Magnanti/Orlin (1993) zu Netzwerkflüssen, Boyd/Vandenberghe (2004) zur konvexen Optimierung, Ehrgott (2005) zur Mehrkriterialität, Birge/Louveaux (2011) zur stochastischen Programmierung. Sie durch jüngere, aber weniger maßgebliche Titel zu ersetzen hätte die Liste verschlechtert. Neuer als 2018 ist konsequent dort, wo sich das Feld tatsächlich bewegt hat: Solver-Benchmarks, Python-Bibliotheken und lernbasierte Optimierung.
-
Neue Themenabschnitte: zwölf. Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: Modellierung in Python, Lineare und ganzzahlige Optimierung, Moderne Solver und Benchmarks, Graphen, Flüsse und Tourenplanung, Metaheuristiken, Spaltengenerierung und Dekomposition, Optimierung unter Unsicherheit, Dynamische Programmierung, Mehrkriterielle Optimierung, Lernbasierte Optimierung, Supply Chain, Energie und Produktionsplanung sowie Praxis: Betrieb, Test und Auslieferung. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung” wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
+
Neue Themenabschnitte: zwölf. Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: Modellierung in Python, Lineare und ganzzahlige Optimierung, Moderne Solver und Benchmarks, Graphen, Flüsse und Tourenplanung, Metaheuristiken, Spaltengenerierung und Dekomposition, Optimierung unter Unsicherheit, Dynamische Programmierung, Mehrkriterielle Optimierung, Lernbasierte Optimierung, Supply Chain, Energie und Produktionsplanung sowie Praxis: Betrieb, Test und Auslieferung. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung” wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
Nicht angelegt wurden zwei erwogene Abschnitte:Diskrete Ereignissimulation und Cloud-basierte Solver-Nutzung. Beide Themen kommen im Buch nicht vor — eine Literaturliste soll das Buch erschließen und nicht darüber hinausweisen. Der API-gestützte Betrieb eines Solvers ist stattdessen im Praxisblock erfasst.
Prüfung gegen Crossref
Alle Einträge wurden einzeln gegen die Crossref-API geprüft — bei einem Literaturverzeichnis ist eine erfundene oder falsch zitierte Quelle der schwerste Fehler, weil der Leser sie sucht und nicht findet.
diff --git a/OR_HTML_04/graphen.html b/OR_HTML_04/graphen.html
index ff1344c..06db036 100644
--- a/OR_HTML_04/graphen.html
+++ b/OR_HTML_04/graphen.html
@@ -184,7 +184,7 @@ Engel -> West (44 min)
Gesamtfahrzeit: 118 min
-
Eine Zeile Code, und das Problem ist beweisbar optimal gelöst — linear_sum_assignment ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden Solver auskommt.
+
Eine Zeile Code, und das Problem ist beweisbar optimal gelöst — linear_sum_assignment ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden Solver auskommt.
Interessanter ist aber, was der Algorithmus nicht tut. Der kleinste Wert der ganzen Tabelle ist die 18 bei Cakir → Nord. Die naheliegende Vorgehensweise — „nimm immer das günstigste noch freie Paar“ — beginnt also genau dort:
@@ -216,7 +216,7 @@ Gesamtfahrzeit: 118 min
🎯 Merksatz Der beste erste Zug ist selten Teil der besten Gesamtlösung. Genau deshalb gibt es Operations Research: Optimierung heißt, Entscheidungen gemeinsam zu treffen statt nacheinander. Bei vier Monteuren kostet die gierige Regel 8 %; bei vierzig kostet sie regelmäßig ein Vielfaches.
-
Warum funktioniert das? Weil das Zuordnungsproblem eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist total unimodular. Das bedeutet — wir kommen in Abschnitt 8.4 darauf zurück —, dass die LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
+
Warum funktioniert das? Weil das Zuordnungsproblem eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist total unimodular. Das bedeutet — wir kommen in Abschnitt 8.4 darauf zurück —, dass die LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
8.2 Lernziele
Nach diesem Kapitel können Sie …
@@ -224,14 +224,14 @@ Gesamtfahrzeit: 118 min
… ein Transportproblem als Graph mit Quellen, Senken und Kapazitäten modellieren.
… den Flusserhaltungssatz aufstellen und seine Bedeutung erklären.
… begründen, warum Zuordnungsprobleme ohne Ganzzahligkeitsbedingung ganzzahlig lösbar sind (totale Unimodularität).
-
… ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
-
… einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
+
… ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
+
… einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
… begründen, warum eine gierige Zuordnung systematisch schlechter ist als eine gemeinsame Optimierung.
… einen Tourenplan gegen die Wirklichkeit prüfen — unabhängig von den Bausteinen, aus denen das Modell gebaut wurde.
8.3 Graphen als Modellsprache
-
Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern GraphenG = (V, E):
+
Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern GraphenG = (V, E):
V — die Knoten (vertices): Server, Depots, Kunden, Konten, Lager.
E — die gerichteten Kanten (edges): Datenleitungen, Straßen, Überweisungswege.
@@ -240,7 +240,7 @@ Gesamtfahrzeit: 118 min
Minimum-Cost Network Flow Problem — deutsch: kostenminimales Flussproblem. Es ist das mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverteilung.
-Abb. 8.1: Das Netzwerk aus Min_Cost_Flow.py samt Lösung: 140 € für 30 Einheiten. Die billige Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 € bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die Dualwerte der Flusserhaltung. Erzeugt von bilder_04/erzeuge_min_cost_flow.py.
+Abb. 8.1: Das Netzwerk aus Min_Cost_Flow.py samt Lösung: 140 € für 30 Einheiten. Die billige Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 € bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die Dualwerte der Flusserhaltung. Erzeugt von bilder_04/erzeuge_min_cost_flow.py.
Sei x_{ij} \ge 0 der Fluss über Kante (i,j), c_{ij} die Kosten je Einheit und u_{ij} die Kapazität:
Satz. Die Extrempunkte der Menge aller doppelt-stochastischen Matrizen (alle Zeilensummen = 1, alle Spaltensummen = 1, x_{ij} \ge 0) sind genau die Permutationsmatrizen (alle x_{ij} \in \{0,1\}).
-
Warum das praktisch enorm wichtig ist: Nach dem Fundamentalsatz aus Kapitel 2 liegt das LP-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
+
Warum das praktisch enorm wichtig ist: Nach dem Fundamentalsatz aus Kapitel 2 liegt das LP-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
🎯 Merksatz Beim Zuordnungsproblem müssen Sie die Ganzzahligkeit nicht fordern — ein gewöhnlicher LP-Solver liefert von selbst eine 0/1-Lösung. Sie sparen sich damit die NP-Schwere von Branch-and-Bound vollständig.
Die praktische Folge in einem Satz:Bei Zuordnungs- und Flussproblemen dürfen Sie die Ganzzahligkeit weglassen und trotzdem ganzzahlige Lösungen erwarten — Sie sparen sich die NP-Schwere von Branch-and-Bound vollständig.
-
Und die Warnung dazu: Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität. Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
+
Und die Warnung dazu: Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität. Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Time Windows (CVRPTW) erweitert es auf eine Flotte mit Kapazitätsgrenzen und Kundenzeitfenstern [e_i, l_i].
+
Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Time Windows (CVRPTW) erweitert es auf eine Flotte mit Kapazitätsgrenzen und Kundenzeitfenstern [e_i, l_i].
Abb. 8.2: Die Lösung der Instanz aus VRP_Flotten_Routing.py: 16 Kunden, vier Fahrzeuge, 619 km. Beachten Sie, dass sich die Touren kreuzen. Bei einem reinen Tourenproblem wäre das ein sicheres Zeichen für eine verbesserbare Lösung — hier ist es keines: Die Zeitfenster erzwingen die Reihenfolge, und wer die Kreuzungen auflöst, kommt zu spät. Erzeugt von bilder_04/erzeuge_vrp_touren.py.
@@ -583,7 +583,7 @@ Umschlag -> Kunde_2 5.0 10 3.00 15.00
Was Sie auf der Karte sehen — und was nicht. Die Instanz ist synthetisch: 17 zufällige Punkte auf einem Quadrat von 100 × 100 km. Für die Karte wurden sie lagegetreu auf eine reale Region gelegt — mit einer azimutal-äquidistanten Projektion, die alle 136 Abstände zwischen den Punkten bis auf 0,001 % erhält. Die Karte zeigt also dieselbe Instanz, mit der das Modell rechnet, nur an geographischen Koordinaten statt auf einem Zahlenraster; kein Wert in diesem Kapitel ändert sich dadurch.
Die Touren sind aus demselben Grund Luftlinien und keine Straßenrouten: Das Modell kennt keine Straßen, es rechnet mit den Abständen der Distanzmatrix. Eine Linie, die einer echten Straße folgte, wäre hübscher und würde eine Genauigkeit vortäuschen, die das Modell nicht hat. Wer Straßenentfernungen braucht, ersetzt die Matrix — Abschnitt 8.7 sagt, was dabei zu beachten ist.
Kurzzyklen verhindern
-
Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt Rangvariablen u_i ein (die Position des Knotens in der Tour):
+
Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt Rangvariablen u_i ein (die Position des Knotens in der Tour):
Abb. 8.3: Links eine Lösung, die jede Gradbedingung erfüllt und trotzdem unbrauchbar ist; rechts dieselbe Instanz mit Kurzzyklus-Bedingungen. Erzeugt von bilder_04/erzeuge_vrp_subtours.py.
@@ -597,7 +597,7 @@ u_i - u_j + C \cdot x_{ij} \le C - d_j \qquad \forall i \ne j
Wird die Kante nicht benutzt (x_{ij} = 0), reduziert sich die Ungleichung auf u_i - u_j \le C - d_j, was durch hinreichend großes C immer erfüllt ist — das Big-M-Muster aus Kapitel 6.
-
💡 In der Praxis: nicht selbst modellieren Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie OR-Tools, spezialisierte Metaheuristiken. Nutzen Sie für Routing die Routing-Bibliothek, nicht ein selbstgebautes MILP.
+
💡 In der Praxis: nicht selbst modellieren Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie OR-Tools, spezialisierte Metaheuristiken. Nutzen Sie für Routing die Routing-Bibliothek, nicht ein selbstgebautes MILP.
Praxisbeispiel: Flotten-Routing
#!/usr/bin/env python3
@@ -766,7 +766,7 @@ u_i - u_j + C \cdot x_{ij} \le C - d_j \qquad \forall i \ne j
if__name__=="__main__": loese_cvrptw()
Min(CumulVar) mit „Ankunftszeit“ verwechseln. Der Solver liefert ein Intervall möglicher Zeiten. Min() ist die früheste, Max() die späteste zulässige Zeit — die tatsächliche Fahrt kann irgendwo dazwischen starten.
Servicezeit im Depot mitzählen. Beim Start am Depot fällt keine Servicezeit an, sonst verschiebt sich der ganze Plan.
@@ -1020,7 +1020,7 @@ plausibel aussieht.
Eine Dimension vergessen. Ladung, Lenkzeit, Kühlkette, Gewicht und Volumen — jede Größe, die begrenzt ist, braucht ihre eigene Dimension. Zählen Sie sie vor dem Modellieren auf einem Blatt Papier auf.
Die Prüfung aus denselben Bausteinen bauen wie das Modell. Wer die Ladung mit loesung.Value(ladungs_dimension.CumulVar(...)) prüft, fragt das Modell, ob es sich an sich selbst hält. Rechnen Sie stattdessen aus der ausgegebenen Tour neu nach.
-
Zwei Läufe nur an der Zielfunktion vergleichen. 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
+
Zwei Läufe nur an der Zielfunktion vergleichen. 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
Unbenutzte Fahrzeuge übersehen. Drei Fahrzeuge, die im Depot stehen, während eines alles fährt, sind fast immer ein Zeichen für eine fehlende Beschränkung.
@@ -1052,14 +1052,14 @@ plausibel aussieht.
Graphen sind die natürliche Sprache für Transport-, Zuordnungs- und Routenprobleme.
Flusserhaltung ist die Kirchhoff-Regel des Operations Research: Was hineingeht, kommt heraus — abzüglich des Knotensaldos.
-
Totale Unimodularität macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier integrality setzt, verschenkt Laufzeit ohne Gegenwert.
+
Totale Unimodularität macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier integrality setzt, verschenkt Laufzeit ohne Gegenwert.
Spezialisierte Algorithmen schlagen allgemeine Solver deutlich: Der Ungarische Algorithmus löst in O(n^3), wofür ein MILP-Solver Branch-and-Bound bräuchte.
Für Routing gilt: Nutzen Sie die Routing-Bibliothek. Metaheuristiken liefern in Sekunden sehr gute Touren; exakte Optimalität ist bei realistischen Größen unrealistisch und praktisch entbehrlich.
Die Routing-Bibliothek kennt nur Dimensionen. Ladung, Lenkzeit, Kühlkette, Gewicht — jede begrenzte Größe braucht ihre eigene. Was nicht als Dimension angelegt ist, wird nicht begrenzt, und der Solver meldet stolz eine kürzere Strecke.
Totale Unimodularität ist zerbrechlich. Eine einzige zusätzliche Bedingung, die nicht ins Schema passt — Kardinalität, Fixkosten, Mindestmenge —, zerstört sie. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
Der beste erste Zug ist selten Teil der besten Gesamtlösung. Gierige Regeln kosten schon bei vier Zuordnungen 8 %.
-
Ausblick.Teil III verlässt die lineare Welt. Kapitel 11 führt quadratische Zielfunktionen und die KKT-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
+
Ausblick.Teil III verlässt die lineare Welt. Kapitel 11 führt quadratische Zielfunktionen und die KKT-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
Danach können Sie: Eine Walk-Forward-Backtest-Architektur bauen, Lookahead-Bias vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
+
Danach können Sie: Eine Walk-Forward-Backtest-Architektur bauen, Lookahead-Bias vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
Lesen Sie diese Tabelle sehr genau. Die zugrunde liegenden Daten sind reiner Zufall — es gibt keinerlei Prognosekraft, kein Signal, keine Struktur. Trotzdem:
Lookahead erzeugt eine scheinbare Überrendite von 15,4 Prozentpunkten pro Jahr. Ein einziger falscher Index — iloc[t+1] statt iloc[t] — verwandelt Rauschen in eine scheinbar brillante Strategie.
-
Survivorship-Bias liefert 13,1 Prozentpunkte allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
+
Survivorship-Bias liefert 13,1 Prozentpunkte allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
Selbst die ehrliche Variante zeigt noch +1,1 Prozentpunkte — reines Rauschen, das aber ohne Weiteres als „leichte Outperformance“ verkauft werden könnte.
Die Lehre: Wenn Sie in einem Backtest eine Überrendite von 15 % sehen, ist die erste Frage nicht „Wie kann ich das nutzen?“, sondern „Wo ist mein Fehler?“. Und wenn Sie keinen finden, suchen Sie weiter — die Wahrscheinlichkeit, dass Sie tatsächlich eine Goldader entdeckt haben, ist deutlich kleiner als die, dass ein Index verrutscht ist.
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten.
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
- Stand: 10. September 2026 v16.11
+ Stand: 10. September 2026 v17.39
diff --git a/OR_HTML_04/lp.html b/OR_HTML_04/lp.html
index 41ea8d4..6af1f88 100644
--- a/OR_HTML_04/lp.html
+++ b/OR_HTML_04/lp.html
@@ -107,17 +107,17 @@
Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil II)
-
🎯 Merksatz zum Diagramm Die erste Frage ist die wichtigste — und sie lautet nicht „welcher Solver ist der schnellste?“, sondern „welche Struktur hat mein Problem?“. Ein Zuordnungsproblem braucht keinen MILP-Solver, sondern eine Zeile linear_sum_assignment. Ein Dienstplan gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
+
🎯 Merksatz zum Diagramm Die erste Frage ist die wichtigste — und sie lautet nicht „welcher Solver ist der schnellste?“, sondern „welche Struktur hat mein Problem?“. Ein Zuordnungsproblem braucht keinen MILP-Solver, sondern eine Zeile linear_sum_assignment. Ein Dienstplan gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
-
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist Teil III der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
+
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist Teil III der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise
📌 Kapitel auf einen Blick
-
Worum geht es? Um das Kernverfahren des Operations Research. Wir bauen den Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher Schattenpreise kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
Danach können Sie: Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein Schattenpreis eine Entscheidung nicht tragen darf.
+
Worum geht es? Um das Kernverfahren des Operations Research. Wir bauen den Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher Schattenpreise kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
Danach können Sie: Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein Schattenpreis eine Entscheidung nicht tragen darf.
Zeitbedarf: ca. 7 Stunden — das umfangreichste Kapitel von Teil II.
Der Betriebsrat hat 20 Überstunden genehmigt. In welchem Bereich sollen sie eingesetzt werden?
-
from scipy.optimize import linprog
+
from scipy.optimize import [linprog](anhang-glossar.md#gloss:linprog){.glossar-link}# Deckungsbeitrag je Stueck (negiert, weil linprog minimiert)c = [-90, -140]
@@ -198,9 +198,9 @@
… eine Simplex-Iteration von Hand rechnen: Pivotspalte, Pivotzeile, Basistausch.
… erklären, warum das Verfahren terminiert und warum es das Optimum findet.
… das duale Problem zu einem gegebenen primalen aufstellen.
-
… Schattenpreise korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle, die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
+
… Schattenpreise korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle, die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
… aus einer Sensitivitätsanalyse eine begründete Kaufentscheidung ableiten.
-
… ein Modell auf Entartung prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
+
… ein Modell auf Entartung prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
… erklären, warum Zulässigkeit immer gegen eine Toleranz und nie mit == 0 geprüft wird.
@@ -251,7 +251,7 @@
Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus Kapitel 2: Das Optimum liegt in einer Ecke. Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
-Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient \mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py.
+Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient\mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py.
Was Sie in der Abbildung sehen. Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
Der Ablauf
@@ -682,7 +682,7 @@ Optimum nach 2 Pivotschritten erreicht.
Negative rechte Seite.-3x_1 - 2x_2 \le -12 (aus einer „\ge“-Bedingung entstanden) macht den Ursprung unzulässig. Dann braucht man Phase 1 mit künstlichen Variablen. Dieser Solver meldet das jetzt ehrlich.
Zyklen bei Entartung. Wenn mehrere Quotienten gleich sind, kann der Simplex theoretisch endlos kreisen. In der Praxis extrem selten; Bland’s Regel verhindert es beweisbar.
-
Den Simplex selbst produktiv einsetzen. Diese Implementierung ist ein Lehrmodell. HiGHS ist um Größenordnungen schneller und numerisch stabiler.
+
Den Simplex selbst produktiv einsetzen. Diese Implementierung ist ein Lehrmodell. HiGHS ist um Größenordnungen schneller und numerisch stabiler.
schattenpreise = -ergebnis.ineqlin.marginals — die eigentliche Korrektur.
Numerische Gegenprobe: Das Modell wird mit b_3 + 1 neu gelöst; der gemessene Zuwachs muss dem Schattenpreis entsprechen. Diese Prüfung ist unabhängig von jeder Vorzeichenkonvention und deshalb der zuverlässigste Test überhaupt.
-
Komplementärer Schlupf:s_i \cdot y_i = 0 muss für alle i gelten.
Übernehmen Sie dieses Muster in eigene Projekte. Der Aufwand ist eine Handvoll Zeilen, der Nutzen ist die Gewissheit, dass Ihre Handlungsempfehlung nicht das Gegenteil des Richtigen sagt.
@@ -1534,7 +1534,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
Aufgabe 5.1 ⭐ — Schlupf deuten. Ein LP liefert s = (0;\ 12{,}5;\ 0;\ 3) und y = (4{,}2;\ 0;\ 9{,}8;\ 0). (a) Welche Ressourcen sind Engpässe? (b) Ist die Lösung mit dem Satz vom komplementären Schlupf verträglich? (c) In welche Ressource würden Sie zuerst investieren?
Aufgabe 5.2 ⭐ — Vorzeichen prüfen. Ein Kollege maximiert Gewinn mit linprog und meldet: „Der Schattenpreis der Engpassmaschine ist -45 €.“ Was ist passiert, und wie lautet der korrekte Wert?
Aufgabe 5.3 ⭐⭐ — Simplex von Hand. Lösen Sie mit dem Tableau-Verfahren vollständig von Hand: \max\ 5x_1 + 4x_2 \quad \text{u. d. N.}\quad 6x_1+4x_2 \le 24,\quad x_1+2x_2 \le 6,\quad x_1,x_2\ge0 Geben Sie jedes Zwischentableau an und lesen Sie am Ende Lösung und Schattenpreise ab. Prüfen Sie mit scipy.optimize.linprog.
-
Aufgabe 5.4 ⭐⭐ — Duales Problem aufstellen. Stellen Sie zum Modell aus der Aufgabe Simplex von Hand das duale Problem auf, lösen Sie es mit linprog und weisen Sie den starken Dualitätssatz numerisch nach.
+
Aufgabe 5.4 ⭐⭐ — Duales Problem aufstellen. Stellen Sie zum Modell aus der Aufgabe Simplex von Hand das duale Problem auf, lösen Sie es mit linprog und weisen Sie den starken Dualitätssatz numerisch nach.
Aufgabe 5.5 ⭐⭐ — Unbeschränktheit erkennen. Was liefert SimplexTableauSolver für \max x_1 + x_2 u. d. N. x_1 - x_2 \le 5, x_1, x_2 \ge 0? Erklären Sie die Fehlermeldung geometrisch.
Aufgabe 5.6 ⭐⭐⭐ — Gültigkeitsbereich des Schattenpreises. Erweitern Sie Sensitivitaetsanalyse.py: Lösen Sie das Modell für Prüfkapazitäten von 30 bis 120 Stunden (Schrittweite 5) und tragen Sie Gewinn und Schattenpreis gegen die Kapazität auf. (a) Ab welcher Kapazität fällt der Schattenpreis auf einen niedrigeren Wert? Warum? (b) Wie viele Stunden sollte der Betrieb bei einem Angebotspreis von 18 €/h maximal zukaufen? (c) Zeichnen Sie den Gewinnverlauf. Was für eine Kurvenform ergibt sich, und warum?
Aufgabe 5.7 ⭐⭐⭐ — Phase 1 ergänzen. Erweitern Sie den SimplexTableauSolver um eine Phase-1-Rechnung mit künstlichen Variablen, sodass auch b_i < 0 verarbeitet werden kann. Testen Sie an: \max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,\quad x_1,x_2\ge0
@@ -1594,7 +1594,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
Schattenpreise beantworten die Managementfrage schlechthin: Wo ist der Engpass, und was ist seine Beseitigung wert?
Vorzeichen prüfen! Nach einer Negation der Zielfunktion müssen die Dualwerte zurücknegiert werden. Die numerische Gegenprobe („Kapazität um 1 erhöhen und neu lösen“) ist die sicherste Kontrolle.
-
Ausblick.Kapitel 6 bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
+
Ausblick.Kapitel 6 bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
Worum geht es? Um das Modell, das 1952 die quantitative Finanzwirtschaft begründete — und um die Restriktionen, ohne die es in der Praxis unbrauchbar bleibt.
Danach können Sie: Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die Korn-Transformation bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
Danach können Sie: Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die Korn-Transformation bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
Bei 3 % Zielrendite: 0,69 — das Portfolio ist sicher, aber lässt Ertrag liegen.
Bei 4 %: 0,75 — das beste Verhältnis.
-
Bei 8 %: 0,44 — das renditestärkste Portfolio ist das schlechteste je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine Diversifikation mehr.
+
Bei 8 %: 0,44 — das renditestärkste Portfolio ist das schlechteste je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine Diversifikation mehr.
Von 3 % auf 8 % Rendite ist ein Zuwachs um Faktor 2,7 — das Risiko steigt dabei um Faktor 3,7. Die letzten Prozentpunkte Rendite sind die teuersten.
🎯 Merksatz Ein Optimierer beantwortet nicht die Frage „was soll ich tun?“, sondern „was ist erreichbar?“. Die Effizienzlinie ist eine Speisekarte, keine Empfehlung. Welche Zeile die richtige ist, hängt von der Risikotragfähigkeit ab — und das ist keine mathematische, sondern eine unternehmerische Frage.
-
Warum funktioniert das? Weil cp.Parameter das Modell nur einmal aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter in CVXPY: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand (Abschnitt 3.8). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
+
Warum funktioniert das? Weil cp.Parameter das Modell nur einmal aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter in CVXPY: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand (Abschnitt 3.8). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
📐 Formel-Lesehilfe Zähler: Überrendite über den risikolosen Zins r_f. Nenner: die Volatilität.
+
📐 Formel-Lesehilfe Zähler: Überrendite über den risikolosen Zins r_f. Nenner: die Volatilität.
Ohne Formel gesagt: „Wie viel Mehrertrag bekomme ich je Einheit Risiko, die ich eingehe?“ Eine Sharpe Ratio von 0,8 heißt: Für jeden Prozentpunkt Volatilität gibt es 0,8 Prozentpunkte Überrendite.
⚠️ Zur Reproduzierbarkeit Dieses Programm lädt Live-Daten. Ihre Zahlen werden abweichen. Prüfen Sie stattdessen die Struktur des Ergebnisses: Hält die Sektorgrenze? Summieren sich die Gewichte auf 100 %? Liegt die Sharpe Ratio des Max-Sharpe-Portfolios über der des GMV? Genau dafür sind die assert-Prüfungen da.
-
🎯 Der Realitätscheck: Gleichgewichtung Das Programm vergleicht immer mit dem 1/N-Portfolio. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen Schätzfehler enthält. Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert. Diese Messlatte sollte in jedem Portfolioprojekt stehen.
+
🎯 Der Realitätscheck: Gleichgewichtung Das Programm vergleicht immer mit dem 1/N-Portfolio. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen Schätzfehler enthält. Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert. Diese Messlatte sollte in jedem Portfolioprojekt stehen.
Aufgabe 19.3 ⭐⭐ — Korn-Transformation nachvollziehen. Zeigen Sie rechnerisch, dass die Sharpe Ratio invariant gegenüber positiver Skalierung ist: \text{SR}(c\,\mathbf{w}) = \text{SR}(\mathbf{w}) für c > 0. Warum gilt das nicht, wenn die Nebenbedingung \sum w_i = 1 mitgeführt wird — und wie löst die Transformation dieses Problem?
Aufgabe 19.4 ⭐⭐ — Restriktionen kosten Rendite. Lassen Sie Markowitz_CVXPY.py mit MAX_GEWICHT von 1,0 (keine Grenze) bis 0,10 in Schritten von 0,05 laufen. Tabellieren Sie Sharpe Ratio und maximal erreichbare Rendite. (a) Was kostet die 20-%-Grenze an Sharpe Ratio? (b) Ab welcher Grenze wird das Problem unlösbar? Warum?
Aufgabe 19.5 ⭐⭐ — Den Sektorfehler nachstellen. Bauen Sie den Fehler bewusst nach: Verwenden Sie tech_indices = [0,1,2,3] auf den alphabetisch sortierten Spalten. Vergleichen Sie das Ergebnis mit der korrekten Version. (a) Welche Titel werden tatsächlich beschränkt? (b) Wie stark unterscheiden sich die Portfoliogewichte? (c) Fällt der Fehler in den ausgegebenen Kennzahlen auf?
-
Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen. Erweitern Sie das Modell um „höchstens K = 5 Titel“ mit Binärvariablen. CVXPY braucht dafür cp.Variable(n, boolean=True) und einen MIQP-fähigen Solver (z. B. SCIP über cp.SCIP oder ECOS_BB). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
+
Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen. Erweitern Sie das Modell um „höchstens K = 5 Titel“ mit Binärvariablen. CVXPY braucht dafür cp.Variable(n, boolean=True) und einen MIQP-fähigen Solver (z. B. SCIP über cp.SCIP oder ECOS_BB). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
Aufgabe 19.7 ⭐⭐⭐ — Out-of-Sample-Test. Teilen Sie die Historie in zwei Hälften. Optimieren Sie auf der ersten, bewerten Sie auf der zweiten. Vergleichen Sie GMV, Max Sharpe und 1/N. (a) Welches Portfolio gewinnt out of sample? (b) Wie ändert sich das Bild mit Ledoit-Wolf gegenüber der Stichprobenkovarianz? (c) Welche Schlussfolgerung ziehen Sie für die Praxis?
19.8 Finde den Denkfehler
-
Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der Nebenbedingung, fällt es schwerer auf.
+
Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der Nebenbedingung, fällt es schwerer auf.
🐛 Finde den Denkfehler: Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio
Worum geht es? Um den Normalfall, den die bisherigen Kapitel umgangen haben: Es gibt nicht ein Ziel, sondern zwei, die gegeneinander stehen. Kosten und CO₂, Termintreue und Bestand, Rendite und Risiko.
Danach können Sie: begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse grundsätzlich nicht erreichen kann; eine vollständige Pareto-Front mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
+
Danach können Sie: begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse grundsätzlich nicht erreichen kann; eine vollständige Pareto-Front mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
Zeitbedarf: ca. 4 Stunden.
Programme: Mehrziel_Pareto.py
@@ -120,7 +120,7 @@
Eine Spedition vergibt fünf Sendungen an LKW, Bahn oder Kombinierten Verkehr. Der LKW ist billig und schmutzig, die Bahn teuer und sauber — und es sind nur zwei Trassen frei.
Statt zu fragen „wie wichtig ist uns CO₂?” fragen wir: Was bekommen wir für zwei Prozent mehr Geld?
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](anhang-glossar.md#gloss:linprog){.glossar-link}# LKW Bahn Kombi LKW billig+schmutzig, Bahn teuer+sauberkosten = np.array([[ 900, 1150, 1020], [ 620, 790, 700], [1300, 1660, 1470],
@@ -637,10 +637,10 @@ dem CO2-Preis, den das Unternehmen ohnehin ansetzt.
Warum es keine Frage des Rasters ist. „c(x) + w\,e(x) minimieren” heißt geometrisch: eine Gerade der Steigung -1/w von links unten an die Punktwolke schieben und schauen, welchen Punkt sie zuerst berührt. Eine Gerade berührt immer einen Eckpunkt der unteren konvexen Hülle. Punkte, die oberhalb der Hülle liegen, werden von keiner Geraden zuerst getroffen — für kein w, bei beliebig feiner Abstufung.
Das Programm rechnet diese Hülle in untere_huelle() unabhängig aus und vergleicht: Genau die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe steht in der Ausgabe.
-
Was Sie in der Abbildung sehen. Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht w. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in Abschnitt 14.7 geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
+
Was Sie in der Abbildung sehen. Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht w. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in Abschnitt 14.7 geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
⚠️ Der Grund liegt in der Ganzzahligkeit
-
Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen ganzzahlig werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
+
Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen ganzzahlig werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
Deshalb betrifft dieses Problem praktisch jedes betriebliche Mehrzielmodell. Wer mit Gewichten arbeitet, verliert nicht ein paar Nachkommastellen, sondern ganze Alternativen — im Beispiel 40 % der Front.
Das Gewicht ist keine Feineinstellung
@@ -717,7 +717,7 @@ e(x) \le \varepsilon
\text{Schattenpreis`\index{Schattenpreis}`{=latex}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
-
Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.
+
Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.
💻 Code-Durchgang: die drei Bausteine
@@ -756,7 +756,7 @@ e(x) \le \varepsilon
Aufgabe 14.1 ⭐ — Dominanz prüfen. Plan P kostet 14 000 € und stößt 6 000 kg aus, Plan Q kostet 13 800 € und stößt 6 100 kg aus. Dominiert einer den anderen? Und wie steht es mit R (14 000 €, 6 100 kg)?
Aufgabe 14.2 ⭐ — Der Schattenpreis in der Praxis. Der interne CO₂-Preis des Unternehmens beträgt 0,90 € je Kilogramm. Welchen Punkt der Front würden Sie empfehlen — und wie begründen Sie ihn in einem Satz?
Aufgabe 14.3 ⭐⭐ — Mehr Trassen. Erhöhen Sie BAHN_TRASSEN von 5 auf 8. Was passiert mit der Länge der Front und mit der Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.
-
Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation. Lassen Sie integrality weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
+
Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation. Lassen Sie integrality weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
Aufgabe 14.5 ⭐⭐⭐ — Drei Ziele. Ergänzen Sie die Laufzeit als drittes Ziel (LKW schnell, Bahn langsam). Die Front wird zu einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand so schnell?
14.10 Finde den Denkfehler
@@ -786,7 +786,7 @@ e(x) \le \varepsilon
14.12 Selbsttest
Erklären Sie Pareto-Dominanz an einem Beispiel aus Ihrem Arbeitsumfeld.
-
Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem MILP nicht?
+
Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem MILP nicht?
Ein Kollege sagt: „Ich nehme w = 0{,}5, das ist neutral.” Was ist daran falsch?
Beschreiben Sie das ε-Constraint-Verfahren in drei Sätzen, ohne Formeln.
Sie legen einer Geschäftsführung eine Front mit zwölf Punkten vor. Welche Spalte brauchen Sie, damit die Entscheidung fallen kann?
Worum geht es? Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der Solver kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
Danach können Sie: eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
+
Worum geht es? Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der Solver kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
Danach können Sie: eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
Ohne Formel gesagt: „Sortiere die Aufträge so, dass möglichst wenig geputzt werden muss.“
-
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein asymmetrisches Rundreiseproblem (Kapitel 8) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
+
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein asymmetrisches Rundreiseproblem (Kapitel 8) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
📖 Definition: Metaheuristik
Ein Verfahrensrahmen, der eine vorhandene Lösung schrittweise verändert und dabei steuert, welche Veränderungen übernommen werden. „Meta“, weil der Rahmen nichts über das Problem weiß: Er braucht nur eine Startlösung, einen Zug und eine Bewertung. Dieselbe Mechanik läuft über Tourenplanung, Personaleinsatz und Portfolioauswahl.
Der Preis dafür steht in derselben Zeile: Ein Verfahren, das nichts über das Problem weiß, kann auch nichts über die Güte seines Ergebnisses sagen. Es liefert eine Lösung, keine Schranke.
Die Instanz
-
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
+
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
Eine Feinheit der Instanz ist wichtiger, als sie aussieht: Die Zahl der Produktfamilien wächst mit der Auftragszahl (eine Familie je zehn Aufträge). Bei fester Familienzahl würde das Problem mit wachsendem n nämlich leichter — der Plan bestünde irgendwann aus ein paar großen Blöcken, und jede Faustregel fände ihn. Beim ersten Entwurf dieses Kapitels ist mir genau das passiert: Die Metaheuristik verbesserte bei 60 Aufträgen um 30 %, bei 200 Aufträgen um 0 %. Nicht weil sie versagte, sondern weil es nichts mehr zu verbessern gab.
9.4 Lokale Suche: der Zug und seine Kosten
@@ -1052,7 +1052,7 @@ Und warum die Starttemperatur ausgemessen gehoert:
print("exakter Lauf dazu, wenn man am Ende die heuristische Loesung einsetzt:")print("nicht wegen seiner Loesung, sondern wegen seiner Schranke.")print("="*88)
-
Erwartete Ausgabe (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
+
Erwartete Ausgabe (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
========================================================================================
AB WANN LOHNT SICH DIE METAHEURISTIK?
========================================================================================
@@ -1106,7 +1106,7 @@ nicht wegen seiner Loesung, sondern wegen seiner Schranke.
60
-
Exakt. CP-SAT beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht.
+
Exakt.CP-SAT beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht.
200
@@ -1128,7 +1128,7 @@ nicht wegen seiner Loesung, sondern wegen seiner Schranke.
9.7 Large Neighborhood Search
Die Tabelle oben endet mit einem Widerspruch. Bei 500 Aufträgen scheitert der exakte Solver an der Größe; die Metaheuristik scheitert an der Kleinheit ihrer Züge — sie verschiebt jeweils einen einzigen Auftrag und kann eine ganze Passage nicht auf einmal umbauen.
-
Large Neighborhood Search (LNS) setzt beide ein, jeden für das, was er kann:
+
Large Neighborhood Search (LNS) setzt beide ein, jeden für das, was er kann:
@@ -1616,7 +1616,7 @@ Abstand zum Optimum.
Wenn es eine natürliche „Kreuzung“ zweier Lösungen gibt. Für Reihenfolgen ist genau das der schwierige Teil. Bibliothek: pymoo.
Bergsteigen, das zuletzt gemachte Züge eine Zeit lang verbietet.
Wenn die Suche zwischen zwei Zuständen pendelt. Oft mit weniger Kalibrierung als Annealing.
@@ -1676,7 +1676,7 @@ Abstand zum Optimum.
9.13 Zusammenfassung
Eine Faustregel ist selten dumm, aber immer kurzsichtig: Sie bewertet einen Schritt danach, was er kostet, nicht danach, was er übrig lässt.
-
Lokale Suche steht und fällt mit der Kostenänderung in O(1). Wer den Zug über die volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
+
Lokale Suche steht und fällt mit der Kostenänderung in O(1). Wer den Zug über die volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
Simulated Annealing nimmt Verschlechterungen mit e^{-\Delta/T} an. T und \Delta haben dieselbe Einheit — die Temperatur gehört an der Zuggröße kalibriert, nicht am Zielfunktionswert. Die verbreitete Regel „20 bis 50 % Annahmequote“ war auf dieser Aufgabe falsch.
Der schlechteste Zwischenwert ist die beste Diagnosezahl eines Annealing-Laufs: Er zeigt eine zu heiße Starttemperatur sofort, ohne dass man das Ergebnis abwarten muss.
Rechnen Sie immer das simple Bergsteigen mit. Hier lieferte es 93 % des Ertrags.
Worum geht es? Um Entscheidungen, die nicht teilbar sind: ganze Maschinen, Ja/Nein, „höchstens fünf davon“. Wir zeigen mit einem konkreten Gegenbeispiel, warum Runden falsch ist, und lernen die Modellierungstricks, mit denen sich logische Regeln in lineare Ungleichungen übersetzen lassen.
Danach können Sie: Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
Danach können Sie: Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
🎯 Merksatz Die LP-Relaxation ist keine ungefähre Antwort, die man nur noch glattziehen muss. Sie ist eine Antwort auf eine andere Frage — nämlich die, bei der man Lager auch zu 14,3 % eröffnen darf. Ihr Wert liegt nicht in ihrer Lösung, sondern in ihrer Schranke: Weniger als 724,14 € kann die richtige Antwort nicht kosten.
-
Genau diese Schranke ist der Hebel, mit dem Branch-and-Bound arbeitet. Wie, zeigt der Abschnitt Abschnitt 6.4.
Der verlockende Gedanke lautet: „Wir lösen das Problem kontinuierlich und runden.“ Eine verbreitete, aber unbelegte Behauptung dazu lautet, man verliere dadurch „oft 20–50 % des Gewinns“. Rechnen wir es nach, statt es zu behaupten.
-Abb. 6.1: Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur Restriktion, also ist die ganze Kante optimal. Aufrunden auf (1;1) führt aus dem zulässigen Bereich heraus, Abrunden auf (0;0) auf den Wert null. Rechts die Messung aus Runden_Gegenbeispiel.py über je 200 Zufallsinstanzen. Erzeugt von bilder_04/erzeuge_runden.py.
+Abb. 6.1: Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur Restriktion, also ist die ganze Kante optimal. Aufrunden auf (1;1) führt aus dem zulässigen Bereich heraus, Abrunden auf (0;0) auf den Wert null. Rechts die Messung aus Runden_Gegenbeispiel.py über je 200 Zufallsinstanzen. Erzeugt von bilder_04/erzeuge_runden.py.
✏️ Handrechnung 6.1: Ein Gegenbeispiel, das Sie im Kopf prüfen können
@@ -366,7 +366,7 @@ Loesung eine Nebenbedingung verletzt. 'Verlust' = Abstand zum exakten Op
6.4 Branch-and-Bound
-
MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen.
+
MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen.
Abb. 6.2: Der Suchbaum der Handrechnung weiter unten — nicht gezeichnet, sondern gerechnet: Das Skript löst die LP-Relaxationen und kappt nach denselben drei Regeln, die oben stehen. Ast B wird verworfen, weil sein Wert 18 den Incumbent 20 aus Ast A nicht schlägt. Erzeugt von bilder_04/erzeuge_branch_and_bound.py.
@@ -576,7 +576,7 @@ Genau hier wuerde Branch-and-Bound verzweigen:
Szenario. Ein Investor verteilt 100\,000\,\text{€} auf sechs Anlageklassen.
Jede Transaktion kostet 50 € Fixgebühr, unabhängig vom Betrag.
-
Höchstens 3 Positionen gleichzeitig (Kardinalitätsbeschränkung).
Wird eine Position eröffnet, dann mit mindestens 10 000 € (Mindestordergröße).
Höchstens 40 000 € pro Position.
@@ -872,7 +872,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
status = status_von_scipy(ergebnis)
-
übersetzt den SciPy-Rückgabewert in die gemeinsame Sprache aus Abschnitt 22.6 — die drei if-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt
+
übersetzt den SciPy-Rückgabewert in die gemeinsame Sprache aus Abschnitt 22.6 — die drei if-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt
schranke=-ergebnis.mip_dual_bound
@@ -884,7 +884,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
-
Warum das Ergebnis wirtschaftlich Sinn ergibt: Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
+
Warum das Ergebnis wirtschaftlich Sinn ergibt: Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
6.8 Wenn der Solver nicht fertig wird: Gap, Zeitlimit und Warm-Start
@@ -1440,7 +1440,7 @@ Instanz Variante Makespan Zeit Faktor
if status == OPTIMAL: ... else: return None. Wirft eine Lösung weg, die 9 % vom Optimum entfernt und damit vollkommen brauchbar ist. Werten Sie den Gap aus, nicht nur den Status.
Zeitlimit ohne Gap-Auswertung. Ein Limit erzwingt ein Ende, sagt aber nichts über die Qualität. Ohne den Gap wissen Sie nicht, ob Sie 0,1 % oder 60 % danebenliegen.
Warm-Start als Selbstverständlichkeit. Messen Sie ihn — auf Ihrer Instanzklasse, gegen den kalten Lauf. In der Hälfte der Fälle bringt er nichts.
-
Einen Hinweis für eine unzulässige Lösung geben. CP-SAT und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
+
Einen Hinweis für eine unzulässige Lösung geben.CP-SAT und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
@@ -1784,7 +1784,7 @@ funktioniert immer.
Runden ist keine Lösung: Aufrunden ist fast immer unzulässig (98–100 % der Fälle), Abrunden verliert im Mittel 10–26 %, im Extremfall alles.
Branch-and-Bound löst LP-Relaxationen, verzweigt an gebrochenen Variablen und kappt Äste anhand von Schranken. Der Preis: NP-Schwere.
Fünf Modellierungsmuster decken die meisten Praxisfälle ab: Fixkosten, Entweder-Oder, Wenn-Dann, Kardinalität, semikontinuierlich.
-
M so klein wie möglich — nicht nur wegen der Laufzeit. Ein übergroßes M kann Binärvariablen bei 10^{-8} hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (Trickle Flow).
+
M so klein wie möglich — nicht nur wegen der Laufzeit. Ein übergroßes M kann Binärvariablen bei 10^{-8} hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (Trickle Flow).
OPTIMAL ist nicht der Normalfall. Werten Sie jeden Status aus und lesen Sie den MIP-Gap — er ist eine Garantie („höchstens 2,3 % vom Optimum“), keine Schätzung. Der Beweis der Optimalität kostet meist ein Vielfaches dessen, was das Finden der optimalen Lösung kostet.
Warm-Starts können nichts verfälschen, aber auch nichts versprechen. Ob ein Startwert hilft, hängt davon ab, ob er besser ist als das, was der Solver in derselben Zeit von allein findet — im Buch gemessen: einmal Faktor 2,2, einmal gar nichts.
Prüfen Sie jede modellierte Regel nach dem Lösen mit einer assert-Zeile — und rechnen Sie den Zielwert aus der Lösung nach, statt ihm zu glauben.
Worum geht es? Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
+
Worum geht es? Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
Voraussetzungen:Kapitel 1 (die vier Bausteine eines Modells). Kein Solverwissen nötig; das ganze Kapitel kommt mit einer einzigen linprog-Zeile aus.
Danach können Sie: eine Anforderung daraufhin abklopfen, was sie nicht sagt; die Zielgröße so wählen, dass sie keinen Fehlanreiz setzt; harte von weichen Regeln unterscheiden — und begründen, warum diese Unterscheidung ein Jahr später über die Betriebsfähigkeit entscheidet.
Zeitbedarf: ca. 3 Stunden.
@@ -167,7 +167,7 @@
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](anhang-glossar.md#gloss:linprog){.glossar-link}# Auftrag: A B C D Estunden = np.array([ 60, 30, 30, 25, 15], dtype=float)
@@ -195,7 +195,7 @@ Deckungsbeitrag maximieren -> BD Umsatz 40,000 DB 19,000
🎯 Merksatz Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.
-
Warum funktioniert das? Weil eine Zielfunktion eine vollständige Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern wertlos. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
+
Warum funktioniert das? Weil eine Zielfunktion eine vollständige Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern wertlos. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
4.2 Lernziele
Nach diesem Kapitel können Sie …
@@ -683,7 +683,7 @@ Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:
Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.
Teil 3: Hart oder weich
-
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
+
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
⚠️ Achtung, Doppelbelegungen. In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: \lambda ist in Kapitel 5 und Kapitel 11 ein Lagrange-Multiplikator/Schattenpreis, in Kapitel 19 und Kapitel 20 ein frei gewählter Risikoaversionsparameter. \gamma ist in Kapitel 13 der Diskontfaktor, in Kapitel 20 die VaR-Hilfsvariable. S bezeichnet als Matrix \mathbf{S} die Stichprobenkovarianz, als Skalar S die Szenarienzahl. \pi ist in Kapitel 9 eine Reihenfolge, in Kapitel 10 ein Dualpreis — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
+
⚠️ Achtung, Doppelbelegungen. In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: \lambda ist in Kapitel 5 und Kapitel 11 ein Lagrange-Multiplikator/Schattenpreis, in Kapitel 19 und Kapitel 20 ein frei gewählter Risikoaversionsparameter. \gamma ist in Kapitel 13 der Diskontfaktor, in Kapitel 20 die VaR-Hilfsvariable. S bezeichnet als Matrix \mathbf{S} die Stichprobenkovarianz, als Skalar S die Szenarienzahl. \pi ist in Kapitel 9 eine Reihenfolge, in Kapitel 10 ein Dualpreis — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
Worum geht es? Warum es für Optimierung in Python mehrere konkurrierende Bibliotheken gibt, was sie unterscheidet, und wie Sie in unter einer Minute die richtige auswählen.
Danach können Sie: Für ein gegebenes Problem begründet einen Solver wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
+
Danach können Sie: Für ein gegebenes Problem begründet einen Solver wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
🚀 In 5 Minuten gelöst: Ein LP ohne jede Installation
+
🚀 In 5 Minuten gelöst: Ein LP ohne jede Installation
Ein Futtermittelhersteller mischt zwei Rohstoffe zu möglichst geringen Kosten. Jeder Kilogramm Mischung muss mindestens 20 g Protein und 5 g Fett enthalten.
@@ -145,8 +145,8 @@
-
Sie brauchen dafür keine zusätzliche Bibliothek — SciPy genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
-
from scipy.optimize import linprog
+
Sie brauchen dafür keine zusätzliche Bibliothek — SciPy genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
+
from scipy.optimize import [linprog](anhang-glossar.md#gloss:linprog){.glossar-link}# linprog MINIMIERT und kennt nur "<=". Mindestgehalte werden also negiert.res = linprog(c=[0.42, 0.88], # Kosten je kg
@@ -158,19 +158,19 @@
print(res.message)print(f"Weizen {res.x[0]:.3f} kg, Soja {res.x[1]:.3f} kg -> {res.fun:.4f} EUR/kg")
Ausgabe:
-
Optimization terminated successfully. (HiGHS Status 7: Optimal)
+
Optimization terminated successfully. ([HiGHS](anhang-glossar.md#gloss:highs){.glossar-link} Status 7: Optimal)
Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
-
Sie haben soeben HiGHS benutzt — denselben C++-Solver, der auch hinter highspy, hinter CVXPY und in vielen kommerziellen Systemen steckt. scipy.optimize.linprog ist nur die dünnste denkbare Hülle darum.
+
Sie haben soeben HiGHS benutzt — denselben C++-Solver, der auch hinter highspy, hinter CVXPY und in vielen kommerziellen Systemen steckt. scipy.optimize.linprog ist nur die dünnste denkbare Hülle darum.
Das ist die zentrale Botschaft dieses Kapitels: Modellierungsschicht und Solver sind zwei verschiedene Dinge. Die Bibliothek, in der Sie Ihr Modell hinschreiben, bestimmt, wie angenehm die Arbeit ist. Der Solver dahinter bestimmt, wie schnell gerechnet wird. Beide lassen sich unabhängig voneinander tauschen — und genau davon handelt der Rest des Kapitels.
⚠️ Zwei Stolpersteine stecken schon in diesen sechs Zeilen
-
linprogminimiert immer. Wer maximieren will, negiert die Zielfunktion — und darf nicht vergessen, das Ergebnis zurückzudrehen.
+
linprogminimiert immer. Wer maximieren will, negiert die Zielfunktion — und darf nicht vergessen, das Ergebnis zurückzudrehen.
linprog kennt nur <=. Ein Mindestgehalt „\ge 20“ wird zu „-12x_1 - 44x_2 \le -20“. Ein Vorzeichenfehler an dieser Stelle liefert eine perfekt aussehende Lösung, die das Gegenteil des Gewollten erfüllt.
-
Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo benutzt, in der >= einfach >= heißt. Das ist ein Hauptgrund, warum es sie gibt.
+
Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo benutzt, in der >= einfach >= heißt. Das ist ein Hauptgrund, warum es sie gibt.
3.2 Lernziele
@@ -181,15 +181,15 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
… dasselbe lineare Programm in scipy.optimize, highspy, ortools und cvxpy formulieren.
… einschätzen, wann sich der Aufwand einer Low-Level-Schnittstelle lohnt und wann nicht.
-
… Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
-
… Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
+
… Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
+
… Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
3.3 Die Zwei-Schichten-Architektur
Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbst. Man nutzt eine zweischichtige Architektur:
Modellierungsschicht (Frontend, DSL): Python-Bibliotheken, mit denen Entscheidungsvariablen, Zielfunktion und Nebenbedingungen in mathematiknaher Syntax formuliert werden. Hier arbeiten Sie.
-
Solver-Schicht (Backend, Engine): Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen (Dual Simplex, Interior-Point, Branch-and-Cut, CDCL-SAT-Suche).
+
Solver-Schicht (Backend, Engine): Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen (Dual Simplex, Interior-Point, Branch-and-Cut, CDCL-SAT-Suche).
@@ -198,7 +198,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
🎯 Merksatz Die Modellierungsschicht bestimmt, wie angenehm Sie arbeiten. Die Solver-Schicht bestimmt, wie schnell gerechnet wird. Beides ist entkoppelt — man kann dieselbe CVXPY-Formulierung mit fünf verschiedenen Solvern lösen.
-
Warum diese Trennung nützlich ist: Ein CVXPY-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
+
Warum diese Trennung nützlich ist: Ein CVXPY-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
3.4 Die Werkzeuge im Vergleich
@@ -225,27 +225,27 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
@@ -258,7 +258,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
Die Entscheidung in drei Fragen
Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:
-
Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen? → Ja: OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → Nein: weiter zu Frage 2.
+
Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen? → Ja:OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → Nein: weiter zu Frage 2.
Frage 2: Ist die Zielfunktion linear? → Ja:scipy.optimize.linprog (klein) oder highspy (groß, wiederholt). → Nein, aber konvex (Quadrate, Normen, log, exp in konvexer Kombination): CVXPY. → Nein und nicht konvex:scipy.optimize.minimize — im Bewusstsein, dass nur ein lokales Optimum herauskommt.
Frage 3: Ist es ein Routing-Problem mit Fahrzeugen, Depots und Zeitfenstern? → Ja: OR-Tools Routing Library (nicht CP-SAT von Hand nachbauen).
@@ -448,7 +448,7 @@ Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
⚠️ max_tasks_per_child=1 ist nicht optional Ein Pool ohne diese Angabe ist der Normalfall — er soll seine Arbeiter ja wiederverwenden. Wer die Isolation über einen Pool herstellt und das vergisst, hat einen Prozesswechsel programmiert, aber keine Isolation gewonnen: Die zweite Aufgabe landet im selben Interpreter wie die erste. Der Absturz kommt dann nicht beim ersten Solver, sondern beim zweiten — und sieht aus wie ein Problem des zweiten.
-
Denselben Aufbau verwenden Solverwechsel_CPSAT_HiGHS.py (Kapitel 22) und Benchmark_Skalierung.py (Kapitel 23). Dort wandern zusätzlich Datenobjekte über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen (Abschnitt 22.6).
+
Denselben Aufbau verwenden Solverwechsel_CPSAT_HiGHS.py (Kapitel 22) und Benchmark_Skalierung.py (Kapitel 23). Dort wandern zusätzlich Datenobjekte über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen (Abschnitt 22.6).
#!/usr/bin/env python3# Ein_System_Vier_Ansaetze.py
@@ -638,7 +638,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
highspy
~15
-
Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen.
+
Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen.
cvxpy
@@ -697,7 +697,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
Vergessen, dass linprog minimiert. Der häufigste Fehler überhaupt. Symptom: Der „optimale“ Gewinn ist erstaunlich niedrig oder null.
CVXPY für ein nicht-konvexes Problem verwenden. CVXPY lehnt das ab mit DCPError: Problem does not follow DCP rules. Das ist ein Feature, keine Einschränkung: Der Fehler sagt Ihnen, dass Ihre Formulierung keine Optimalitätsgarantie hätte.
CP-SAT mit kontinuierlichen Variablen füttern. CP-SAT kennt nur ganze Zahlen. Wer Euro-Beträge modelliert, rechnet in Cent (Ganzzahl) — oder nimmt einen LP-Solver.
-
Für jeden Lauf ein neues Modell bauen. Bei 60 Backtest-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. highspy und CVXPY-Parameter erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe Kapitel 19 und Kapitel 21).
+
Für jeden Lauf ein neues Modell bauen. Bei 60 Backtest-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. highspy und CVXPY-Parameter erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe Kapitel 19 und Kapitel 21).
@@ -729,7 +729,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
🐛 Finde den Denkfehler: Der Solver, der angeblich dreimal schneller ist
-
Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins Protokoll: „CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“ Gemessen wurde so:
+
Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins Protokoll: „CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“ Gemessen wurde so:
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
-
1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt? (a) CVXPY — es prüft die Konvexität automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) scipy.optimize.minimize — es kann beliebige Zielfunktionen.
+
1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt? (a) CVXPY — es prüft die Konvexität automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) scipy.optimize.minimize — es kann beliebige Zielfunktionen.
2. Ihr Modell mit 80 000 Variablen braucht 40 Sekunden, davon 32 Sekunden bis zum ersten Solver-Aufruf. Was ist die wirksamste Maßnahme? (a) Einen kommerziellen Solver lizenzieren. (b) Das Zeitlimit des Solvers heraufsetzen. (c) Den Modellaufbau vektorisieren — der Solver ist mit 8 Sekunden gar nicht der Engpass.
-
3. Was haben scipy.optimize.linprog, highspy, CVXPY (mit Standardeinstellung) und Pyomo (mit appsi_highs) gemeinsam? (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
+
3. Was haben scipy.optimize.linprog, highspy, CVXPY (mit Standardeinstellung) und Pyomo (mit appsi_highs) gemeinsam? (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
🚀 In 5 Minuten gelöst: Die Abnahmeprüfung, die in jedes Modell gehört
def pruefe_loesung(x, A, b, kosten, zielwert, ganzzahlig=(), toleranz=1e-6):
-"""Prueft eine Loesung gegen die Anforderungen - OHNE den Solver zu fragen."""
+"""Prueft eine Loesung gegen die Anforderungen - OHNE den [Solver](anhang-glossar.md#gloss:solver){.glossar-link} zu fragen.""" beanstandungen = [] verbrauch = A @ x # 1. Werden Grenzen eingehalten?
@@ -193,13 +193,13 @@
… die fünf typischen Praxisfallen benennen und je ein Gegenmittel nennen.
… ein unlösbares Modell in einen „am wenigsten schlechten“ Plan verwandeln.
… für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der Schattenpreis diese Frage nur für die nächste Einheit beantwortet.
… für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der Schattenpreis diese Frage nur für die nächste Einheit beantwortet.
… Zeitlimits und Optimalitätslücken sinnvoll setzen.
… die Architektur einer produktionsreifen OR-Plattform skizzieren.
… eine modellunabhängige Abnahmeprüfung schreiben, die eine Lösung gegen die Anforderungen prüft statt gegen das Modell.
… Status, Gap und Zeitausschöpfung protokollieren und begründen, warum eine konstante Laufzeit bei Zeitlimit ein Warnsignal ist.
-
… ein Modell in Domänenschicht, Modellbauer und Lösungs-DTO trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
+
… ein Modell in Domänenschicht, Modellbauer und Lösungs-DTO trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
… die Statuswerte verschiedener Solverbibliotheken auf eine gemeinsame Sprache abbilden.
@@ -209,10 +209,10 @@
Abb. 22.1: Die fünf typischen Praxisfallen
Falle 1 — Infeasibility
-
Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE.
+
Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE.
Falsch: Das System stürzt ab oder zeigt „Fehler“. Der Disponent steht ohne Plan da.
-
Richtig:Hierarchische Relaxation. Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, wo es klemmt.
+
Richtig:Hierarchische Relaxation. Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, wo es klemmt.
Ein Planer oder Trader lehnt ein System ab, wenn er die Zuweisungen nicht nachvollziehen kann. Akzeptanz ist keine Nebensache, sondern Voraussetzung für den Betrieb.
„Warum bekommt Person A die Stunde und nicht Person B?“ → „Weil B heute bereits zwei Vertretungsstunden hat und bei einer dritten die gesetzliche Ruhezeit von 11 Stunden unterschritten würde. A hat 80 Strafpunkte weniger.“
-
Bei Portfolioentscheidungen: Kostenzerlegung ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in Kapitel 7 und Kapitel 20).
+
Bei Portfolioentscheidungen: Kostenzerlegung ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in Kapitel 7 und Kapitel 20).
🎯 Die Faustregel für Erklärbarkeit Für jede Entscheidung sollten Sie drei Fragen beantworten können: 1. Warum diese Lösung? — Zielfunktionswert und seine Bestandteile. 2. Warum nicht die naheliegende Alternative? — Welche Bedingung verhindert sie? 3. Was würde die Lösung verbessern? — Schattenpreise: welcher Engpass, welcher Wert.
Kapitel 5 liefert für Frage 3 das Werkzeug. Nutzen Sie es — Schattenpreise sind in Managementgesprächen oft wertvoller als die Lösung selbst.
@@ -446,14 +446,14 @@
Falle 3 — Regimewechsel und Schätzfehler
Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine Liquiditätskrise und keine Zinswende.
Harte Obergrenzen für Einzelpositionen und Sektoren — auch dann, wenn der Solver rechnerisch alles in einen Titel legen möchte. Die Grenze kostet Ertrag im Normalfall und rettet im Ernstfall.
Ausführlich in Kapitel 21 behandelt. Die Kurzfassung: Beide Fehler machen Backtests systematisch zu gut, ohne eine Fehlermeldung zu erzeugen. Der Lookahead-Selbsttest aus Abschnitt 21.5 findet den ersten automatisch.
Falle 5 — Laufzeitexplosion
-
Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
+
Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
22.4 Constraint Attribution: welche Bedingung kostet wie viel?
-
Der Constraint-Trace beantwortet „warum diese Zuweisung?“. Der Deletion Filter in Anhang C beantwortet „warum geht es gar nicht?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
+
Der Constraint-Trace beantwortet „warum diese Zuweisung?“. Der Deletion Filter in Anhang C beantwortet „warum geht es gar nicht?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
Das Modell rechnet. Der Plan ist zulässig. Und trotzdem ist er enttäuschend — nur weiß niemand, woran es liegt.
-
Die Antwort heißt Constraint Attribution: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit Kosten statt Zulässigkeit als Kriterium.
+
Die Antwort heißt Constraint Attribution: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit Kosten statt Zulässigkeit als Kriterium.
Gerechnet wird auf derselben Fabrik wie die Konfliktsuche in Anhang C, aber nach der Reparatur: Dort war die Lackierkapazität von 150 Stunden der Kern des Widerspruchs (2 \cdot 40 + 3 \cdot 30 = 170 > 150). Eine zweite Schicht hat sie auf 210 gebracht, der Mindestumsatz wurde auf 10 000 € gesenkt. Jetzt ist das Modell lösbar — und die Frage lautet nicht mehr „warum nicht?“, sondern „was kostet uns was?“.
#!/usr/bin/env python3
@@ -908,7 +908,7 @@ aus.
==============================================================================
Drei Befunde, und keiner steht im Solverergebnis
-
Erstens: bindend heißt nicht teuer. Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von 0,00 €. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den Erklaerbarkeit.py oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines LP.
+
Erstens: bindend heißt nicht teuer. Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von 0,00 €. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den Erklaerbarkeit.py oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines LP.
Zweitens: Der Schattenpreis ist eine Momentaufnahme. Er gilt für die nächste Einheit. Ein Planer kauft aber keine Einheit, sondern eine Schicht. Für die Lackiererei verspricht die Hochrechnung 4{,}00 \cdot 30 = 120 €; gemessen kommen 60 €. Der Preis von 4,00 € gilt exakt bis +15 Stunden, danach begrenzt etwas anderes — die zweite Hälfte der Sonderschicht wäre bezahlt und wirkungslos.
Und die Ranglisten drehen sich dabei um: Nach Schattenpreis führt der Trägervertrag mit 18,00 € je Stück, nach tatsächlicher Wirkung die Montage-Sonderschicht mit 450 €. Der Schattenpreis sagt, was eine Einheit wert ist — nicht, wie viele Einheiten zu haben sind. Für die Frage „welchen Hebel ziehen wir?“ ist das der entscheidende Unterschied.
Drittens: Ein Wunsch kann unmöglich statt teuer sein. Der Vertrieb hätte gern 55 Rahmen statt 47,5. Das Modell antwortet nicht mit einem Preis, sondern mit INFEASIBLE — und drei Bedingungen blockieren den Wunsch gemeinsam: Fällt eine davon weg, ist er erfüllbar, keine allein ist „der“ Grund. Genau die Struktur, die Anhang C für den unlösbaren Fall beschreibt, hier auf einen Wunsch angewandt.
Was Sie in der Abbildung sehen. Fünf der sechs Schichten bilden einen Weg: Daten gehen oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut sie am Ende auch so: als etwas, das man hinterher noch macht.
Fünf Prinzipien, die sich in der Praxis bewährt haben:
-
Snapshot-Prinzip. Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
-
Zustandslosigkeit. Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
-
Mensch in der Schleife. Der Optimierer schlägt vor, ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
-
Fallback-Strategie. Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
+
Snapshot-Prinzip. Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
+
Zustandslosigkeit. Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
+
Mensch in der Schleife. Der Optimierer schlägt vor, ein Mensch entscheidet — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
+
Fallback-Strategie. Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
Versionierung. Modellversion, Parametersatz und Solver-Version gehören ins Protokoll jedes Laufs. Ohne das lässt sich nicht klären, warum das Ergebnis von letzter Woche anders aussah.
Wer or_kern nur für Statusauswertung und Prüfung benutzt, soll pandas nicht installieren müssen.
+
Wer or_kern nur für Statusauswertung und Prüfung benutzt, soll pandas nicht installieren müssen.
schattenpreise im DTO
@@ -1710,7 +1710,7 @@ Domaenenschicht.
22.7 Der Solverwechsel in der Praxis
Der vorige Abschnitt endet mit einer Behauptung: Ein Solverwechsel betreffe genau einen Baustein. Das ist die Art Satz, die in jedem Architekturvortrag vorkommt und die man deshalb nicht glauben sollte, bevor sie jemand vorgeführt hat.
-
Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: CP-SAT gegen HiGHS. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren (Kapitel 3).
+
Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: CP-SAT gegen HiGHS. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren (Kapitel 3).
Die Aufgabe ist eine kapazitierte Standortplanung: Aus sechs möglichen Lagern sind diejenigen zu eröffnen, mit denen zwölf Kunden am günstigsten beliefert werden. Jeder Kunde wird von genau einem Lager bedient, jedes eröffnete Lager kostet eine Fixgebühr, und die Kapazität reicht je Lager für 174 der insgesamt 387 Paletten — es müssen also mindestens drei Lager öffnen.
Der Aufbau folgt dem Muster aus or_kern.py:
@@ -2141,16 +2141,16 @@ ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).
22.8 Eine Checkliste vor dem Produktivgang
Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
-
Modell - [ ] Alle Nebenbedingungen sind dokumentiert — mit fachlicher Begründung, nicht nur als Code. - [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart sein? - [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall. - [ ] Big-M-Werte sind so klein wie möglich gewählt. - [ ] Die Zielfunktion ist in konsistenten Einheiten formuliert.
+
Modell - [ ] Alle Nebenbedingungen sind dokumentiert — mit fachlicher Begründung, nicht nur als Code. - [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart sein? - [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall. - [ ] Big-M-Werte sind so klein wie möglich gewählt. - [ ] Die Zielfunktion ist in konsistenten Einheiten formuliert.
Daten - [ ] Spaltenreihenfolge und Datentypen werden nach jedem Import geprüft. - [ ] Fehlende Werte und Ausreißer haben eine definierte Behandlung. - [ ] Kovarianzmatrizen werden auf positive Definitheit geprüft. - [ ] Datenstände sind versioniert und reproduzierbar.
Validierung - [ ] Das Ergebnis wurde gegen eine unabhängige Rechnung geprüft (Handrechnung, zweiter Solver, Simulation). - [ ] Alle Nebenbedingungen werden nach dem Lösen per assert verifiziert. - [ ] Es gibt einen Vergleich gegen eine naive Referenzstrategie. - [ ] Bei Backtests: Lookahead-Selbsttest bestanden, Zahl der Versuche protokolliert.
Betrieb - [ ] Zeitlimit und akzeptierter Gap sind festgelegt und begründet. - [ ] Es gibt eine Fallback-Strategie und Alarmierung. - [ ] Jedes Ergebnis ist mit Snapshot-ID, Modellversion und Solver-Version protokolliert. - [ ] Die Ergebnisse sind erklärbar (Kostenzerlegung, Schattenpreise, Constraint-Trace).
22.9 Weiterführende Literatur und Roadmap
-
Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Integer Programming — Branch-and-Cut, Schnittebenen, Formulierungsstärke.
+
Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Integer Programming — Branch-and-Cut, Schnittebenen, Formulierungsstärke.
Konvexe Optimierung * Boyd & Vandenberghe: Convex Optimization — Pflichtlektüre; frei verfügbar. Die theoretische Basis von CVXPY.
-
Constraint Programming und Scheduling * Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
-
Quantitative Finanzmathematik * López de Prado: Advances in Financial Machine Learning — Backtest-Overfitting, Denoising, Deflated Sharpe Ratio. * Cornuéjols, Peña & Tütüncü: Optimization Methods in Finance.
+
Constraint Programming und Scheduling * Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
+
Quantitative Finanzmathematik * López de Prado: Advances in Financial Machine Learning — Backtest-Overfitting, Denoising, Deflated Sharpe Ratio. * Cornuéjols, Peña & Tütüncü: Optimization Methods in Finance.
Wie es weitergeht
Der nächste Schritt nach diesem Kurs ist selten ein weiteres Verfahren, sondern die Verankerung im Betrieb: reproduzierbare Datenpipelines, versionierte Modelle, Monitoring der Lösungsqualität, bewusster Umgang mit Zeitlimits.
Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen:
@@ -2169,27 +2169,27 @@ ein unzuverlaessiger Test (siehe JobShop_Intervalle.py).
Aufgabe 22.1 ⭐ — Hart oder weich, revisited. Nennen Sie für ein Vertretungsplanungssystem je zwei Bedingungen, die (a) zwingend hart bleiben müssen, (b) unbedingt weich sein sollten, (c) diskutabel sind. Begründen Sie.
-
Aufgabe 22.2 ⭐ — Zeitlimit wählen. Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-Rebalancing monatlich.
-
Aufgabe 22.3 ⭐⭐ — Relaxation einbauen. Nehmen Sie Ihr Modell aus der Aufgabe Eigener Dienstplan (Abschnitt 7.9) und machen Sie es INFEASIBLE-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
+
Aufgabe 22.2 ⭐ — Zeitlimit wählen. Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-Rebalancing monatlich.
+
Aufgabe 22.3 ⭐⭐ — Relaxation einbauen. Nehmen Sie Ihr Modell aus der Aufgabe Eigener Dienstplan (Abschnitt 7.9) und machen Sie es INFEASIBLE-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
Aufgabe 22.4 ⭐⭐ — Erklärbarkeit implementieren. Erweitern Sie ein beliebiges Modell aus dem Kurs um einen erklaere(loesung)-Report, der die drei Fragen aus Abschnitt 22.3 beantwortet.
Aufgabe 22.5 ⭐⭐ — Den Bericht in die Irre führen.Constraint_Attribution.py erzeugt seinen Bericht aus gerechneten Zahlen — richtig ist er deshalb noch nicht. (a) Setzen Sie den Deckungsbeitrag von Deckel von 9 € auf 20 €. Wie ändern sich Plan, Rangliste und Bericht? Welche Sätze bleiben wörtlich stehen, obwohl sie jetzt etwas anderes bedeuten? (b) Der Bericht nennt „Liefervertrag Träger” als teuerste Bindung. Formulieren Sie den Satz so um, dass ein Leser erkennt, worauf die Aussage beruht. (c) Welche Angabe müsste das Programm zusätzlich ausgeben, damit ein Empfänger die Belastbarkeit selbst einschätzen kann?
Aufgabe 22.6 ⭐⭐⭐ — Gap gegen Laufzeit. Nehmen Sie das MILP-Portfolio aus Kapitel 6 und vergrößern Sie es auf 50 Anlagen. Messen Sie Laufzeit und Zielwert bei Gap-Vorgaben von 10 %, 5 %, 2 %, 1 %, 0,1 % und 0 %. Stellen Sie den Zusammenhang grafisch dar und leiten Sie eine Empfehlung ab.
-
Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben. Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle in Abschnitt 5.7, die ungültige Kovarianzmatrix in Kapitel 11, die Sektor-Positionsindizes in Kapitel 19, die vermischten Einheiten in Abschnitt 20.6 oder die Rebalancing-Termine in Abschnitt 21.4) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
+
Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben. Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle in Abschnitt 5.7, die ungültige Kovarianzmatrix in Kapitel 11, die Sektor-Positionsindizes in Kapitel 19, die vermischten Einheiten in Abschnitt 20.6 oder die Rebalancing-Termine in Abschnitt 21.4) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
22.11 Finde den Denkfehler
-
Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts.
+
Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts.
🐛 Finde den Denkfehler: Das Modell, das seit einem Jahr nicht mehr optimiert
@@ -2501,7 +2501,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
1. Ihr Nachtjob läuft seit Monaten konstant 3,0 Sekunden bei einem Zeitlimit von 3 Sekunden. Was schließen Sie daraus? (a) Das System ist bemerkenswert stabil. (b) Das Zeitlimit greift jedes Mal. Der Solver bricht ab, statt fertig zu werden — die konstante Laufzeit ist das Symptom, nicht die Beruhigung. (c) Der Rechner ist ausgelastet und sollte aufgerüstet werden.
2. Eine Prüffunktion soll kontrollieren, ob ein Tourenplan die Fahrzeugkapazitäten einhält. Woher nimmt sie die geladenen Mengen? (a) Aus der Ladungsdimension des Routing-Modells — dort stehen sie ja bereits. (b) Sie summiert die Bedarfe der Kunden aus dem ausgegebenen Tourenplan auf. Alles andere fragt das Modell, ob es sich an sich selbst hält. (c) Aus der Zielfunktion, sofern die Kapazität dort bepreist ist.
-
3. Ein Modell meldet INFEASIBLE, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt? (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten statt Verboten —, damit der Solver benennt, welche Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
+
3. Ein Modell meldet INFEASIBLE, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt? (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten statt Verboten —, damit der Solver benennt, welche Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
@@ -2521,7 +2521,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
22.14 Zusammenfassung
-
Infeasibility ist ein Entwurfsproblem, kein Solverproblem. Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
+
Infeasibility ist ein Entwurfsproblem, kein Solverproblem. Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
Erklärbarkeit entscheidet über Akzeptanz. Schattenpreise, Kostenzerlegung und Constraint-Traces sind keine Kür.
Bindend heißt nicht teuer. Eine Bedingung kann den Plan berühren und trotzdem 0,00 € kosten. Nur die kostenrelevanten lohnen eine Nachverhandlung.
Der Schattenpreis gilt für die nächste Einheit, nicht für die nächste Schicht. Hochgerechnet auf einen realistischen Schritt überschätzt er den Nutzen — im Beispiel um das Doppelte. Wer wissen will, was ein Hebel bringt, misst die Lockerung, statt zu multiplizieren; die beiden Ranglisten können sich dabei umdrehen.
Worum geht es? Um die Naht zwischen zwei Welten, die in getrennten Abteilungen sitzen: Ein Modell prognostiziert, ein anderes entscheidet. Beide arbeiten sauber — und genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.
-
Voraussetzungen:Kapitel 12, insbesondere das Newsvendor-Problem und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
-
Danach können Sie: begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige Zielgröße einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
+
Voraussetzungen:Kapitel 12, insbesondere das Newsvendor-Problem und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
+
Danach können Sie: begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige Zielgröße einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein Fehlermaß — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
+
Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein Fehlermaß — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
Der Fehler steckt nicht in einer der beiden Hälften, sondern in der Naht:
Das übliche zweistufige Vorgehen: erst eine unbekannte Größe schätzen, dann mit der Schätzung optimieren, als wäre sie die Wahrheit.
Das Problem: Die erste Stufe wird auf ein statistisches Maß trainiert, die zweite erzeugt ökonomische Kosten. Niemand garantiert, dass ein besseres statistisches Maß zu geringeren Kosten führt — und in diesem Kapitel ist es nachweislich umgekehrt.
@@ -654,7 +654,7 @@ Kosten statt auf Fehler trainiert.
15.7 Wie weit das Verfahren reicht
Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert” — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: das Prognosemodell direkt auf die Entscheidungskosten zu trainieren statt auf ein Fehlermaß.
-
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man durch das Optimierungsproblem hindurch ableiten.
+
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man durch das Optimierungsproblem hindurch ableiten.
@@ -707,7 +707,7 @@ Kosten statt auf Fehler trainiert.
🐛 „Wir haben die Prognose um 18 % verbessert”
Ein Data-Science-Team stellt sein Quartalsergebnis vor:
-
„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte.”
+
„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte.”
Sechs Wochen später meldet die Disposition, die Retouren seien gestiegen.
Das Team prüft alles nach: Die Modellgüte stimmt, die Testdaten waren sauber getrennt, es gibt kein Leck. Das neue Modell prognostiziert die Nachfrage tatsächlich deutlich besser als das alte.
Ausgangslage. Morgens um 7:15 Uhr meldet sich die dritte Lehrkraft krank. Die stellvertretende Schulleitung hat 20 Minuten, um einen Vertretungsplan zu erstellen, der Qualifikationen, Arbeitszeiten, bereits geleistete Vertretungen und persönliche Wünsche berücksichtigt.
Daten. Stundenplan (CSV: Klasse, Tag, Stunde, Fach, Lehrkraft, Raum) — in diesem Projektordner erzeugen die Skripte stundenplan*.py genau solche Dateien und eignen sich als Datenquelle. Zusätzlich: Qualifikationsmatrix, Deputate, Abwesenheitsmeldungen.
-
Modellskizze. * Variablen: x_{p,s} \in \{0,1\} — Person p übernimmt Slot s. * Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl Vertretungsstunden; Ruhezeiten. * Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche. * Solver: CP-SAT.
-
Abnahmekriterien. - [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots). - [ ] Liefert immer einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation). - [ ] Weist je Zuweisung eine Begründung aus. - [ ] Ausgabe als CSV und als lesbare Tagestabelle. - [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
+
Modellskizze. * Variablen: x_{p,s} \in \{0,1\} — Person p übernimmt Slot s. * Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl Vertretungsstunden; Ruhezeiten. * Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche. * Solver: CP-SAT.
+
Abnahmekriterien. - [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots). - [ ] Liefert immer einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation). - [ ] Weist je Zuweisung eine Begründung aus. - [ ] Ausgabe als CSV und als lesbare Tagestabelle. - [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
Stolperfallen. * Zu viele harte Regeln → INFEASIBLE an genau dem Tag, an dem man das System braucht. * Fairness nur über einen Tag statt über die Woche → dieselbe Person trifft es ständig. * Akzeptanz: Ohne Begründungsanzeige wird der Plan überschrieben.
Erweiterungen. Mehrtagesplanung; Raumkonflikte; Springerstunden; Schnittstelle zum Schulverwaltungsprogramm.
Ausgangslage. 25 Pflegekräfte, drei Schichten täglich, 28-Tage-Zyklus. Es gelten Arbeitszeitgesetz, Tarifvertrag, Qualifikationsmix und individuelle Wünsche.
-
Modellskizze. * Variablen: x_{p,t,s} \in \{0,1\} — Person, Tag, Schicht. * Hart: Mindestbesetzung je Schicht und Qualifikationsmix (mindestens eine examinierte Kraft); nach Nachtschicht ≥ 2 freie Tage; höchstens 6 Arbeitstage in Folge; monatliche Sollstunden ± 10 %. * Weich: Wunschfrei, gleichmäßige Wochenendverteilung, ungeteilte Dienste, stabile Schichtfolgen (nicht Früh–Nacht–Früh). * Solver: CP-SAT mit Intervall- und Cumulative-Constraints.
-
Abnahmekriterien. - [ ] Alle gesetzlichen Regeln nachweislich eingehalten (per assert geprüft). - [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt (Gini-Koeffizient). - [ ] Läuft in unter 60 Sekunden. - [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
-
Stolperfallen. Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — Symmetriebrechung und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
+
Modellskizze. * Variablen: x_{p,t,s} \in \{0,1\} — Person, Tag, Schicht. * Hart: Mindestbesetzung je Schicht und Qualifikationsmix (mindestens eine examinierte Kraft); nach Nachtschicht ≥ 2 freie Tage; höchstens 6 Arbeitstage in Folge; monatliche Sollstunden ± 10 %. * Weich: Wunschfrei, gleichmäßige Wochenendverteilung, ungeteilte Dienste, stabile Schichtfolgen (nicht Früh–Nacht–Früh). * Solver: CP-SAT mit Intervall- und Cumulative-Constraints.
+
Abnahmekriterien. - [ ] Alle gesetzlichen Regeln nachweislich eingehalten (per assert geprüft). - [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt (Gini-Koeffizient). - [ ] Läuft in unter 60 Sekunden. - [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
+
Stolperfallen. Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — Symmetriebrechung und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
P3 — Tourenplanung für einen Lieferdienst
@@ -195,7 +195,7 @@
Ausgangslage. Ein regionaler Lieferdienst fährt täglich 40–80 Adressen mit 4 Fahrzeugen an. Kunden haben Zeitfenster, Fahrzeuge Kapazitäten, Fahrer Arbeitszeiten.
Daten. Adressen aus einer CSV; Entfernungen über OpenStreetMap (osmnx, openrouteservice) oder als Luftlinie mit Umwegfaktor 1,3 als Näherung.
-
Modellskizze. OR-Tools Routing-Bibliothek mit Kapazitäts- und Zeitdimension; Metaheuristik GUIDED_LOCAL_SEARCH, Zeitlimit 30 Sekunden.
Abnahmekriterien. - [ ] Alle Kunden werden innerhalb ihrer Zeitfenster beliefert. - [ ] Vergleich gegen die bisherige manuelle Tourenplanung: Ersparnis in km und Minuten. - [ ] Kartendarstellung der Touren (folium). - [ ] Robust gegen Ausfall eines Fahrzeugs (Neuplanung in unter 30 s).
Stolperfallen. Luftlinie unterschätzt Fahrzeiten systematisch — mit realistischem Umwegfaktor arbeiten oder echte Routing-Distanzen holen. Zeitfenster, die physisch nicht erreichbar sind, führen zu „keine Lösung“ ohne Erklärung: Vorabprüfung einbauen (siehe Kapitel 8).
@@ -205,14 +205,14 @@
Ausgangslage. Ein Handelsunternehmen prüft, welche von 12 möglichen Lagerstandorten eröffnet werden sollen, um 60 Filialen zu versorgen.
Modellskizze. Kombiniertes Standort- und Transportproblem: * y_j \in \{0,1\} — Lager j eröffnen (Fixkosten). * x_{ij} \ge 0 — Menge von Lager j zu Filiale i. * Kopplung: x_{ij} \le M\,y_j; Kapazität je Lager; Bedarfsdeckung je Filiale. * Ziel: Fixkosten + Transportkosten minimieren.
-
Abnahmekriterien. - [ ] Sensitivitätsanalyse: Wie verändert sich die Lösung bei ±20 % Transportkosten? - [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das? - [ ] Kartendarstellung mit Zuordnungslinien. - [ ] Amortisationsrechnung über 10 Jahre.
+
Abnahmekriterien. - [ ] Sensitivitätsanalyse: Wie verändert sich die Lösung bei ±20 % Transportkosten? - [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das? - [ ] Kartendarstellung mit Zuordnungslinien. - [ ] Amortisationsrechnung über 10 Jahre.
Stolperfallen. Big-M zu groß wählen (Kapitel 6) — hier ist die Lagerkapazität die natürliche Wahl. Fixkosten sind einmalig, Transportkosten laufend: Barwerte rechnen, nicht einfach addieren.
Ausgangslage. Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet Rüstzeit; die Rüstzeit hängt von der Reihenfolge ab.
+
Ausgangslage. Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet Rüstzeit; die Rüstzeit hängt von der Reihenfolge ab.
Modellskizze. Los- und Reihenfolgeplanung: * Variablen: Produktionsmengen, Rüstentscheidungen, Reihenfolge (Intervallvariablen). * Hart: Bedarfsdeckung je Periode, Maschinenkapazität, Mindestlosgrößen. * Ziel: Rüst- + Lager- + Fehlmengenkosten minimieren. * Solver: CP-SAT mit AddNoOverlap und reihenfolgeabhängigen Übergangszeiten.
Abnahmekriterien. - [ ] Gantt-Diagramm der Maschinenbelegung. - [ ] Vergleich gegen die aktuelle Praxis (Ersparnis in Rüststunden). - [ ] Schattenpreise: Welche Maschine ist der Engpass, was wäre eine zusätzliche Schicht wert?
@@ -222,8 +222,8 @@
⚠️ Keine Anlageberatung. Dieses Projekt dient dem Methodenverständnis. Setzen Sie kein echtes Geld auf ein selbstgebautes Modell, dessen Grenzen Sie nicht vollständig verstehen.
Ausgangslage. Ein Privatdepot aus 10–15 ETFs und Einzeltiteln soll quartalsweise zurückgeführt werden — unter Berücksichtigung von Ordergebühren, Mindestordergrößen und der Steuerfreibetragsnutzung.
-
Modellskizze. * Variablen: Zielgewichte w_i; Binärvariablen für „Position wird gehandelt“. * Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, Kapitel 6), höchstens K Transaktionen je Rebalancing. * Ziel: erwartete Rendite − Risikoterm − Transaktionskosten − Steuerwirkung. * Solver: CVXPY (konvexer Teil) oder MIQP für die Kardinalität.
-
Abnahmekriterien. - [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt. - [ ] Alle Kosten (Ordergebühr, Spread, Steuer) sind explizit ausgewiesen. - [ ] Backtest über 5 Jahre mit Lookahead-Selbsttest. - [ ] Vergleich gegen: nie rebalancen, jährlich rebalancen, Gleichgewichtung. - [ ] Bericht, den ein Nicht-Fachmann versteht.
+
Modellskizze. * Variablen: Zielgewichte w_i; Binärvariablen für „Position wird gehandelt“. * Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, Kapitel 6), höchstens K Transaktionen je Rebalancing. * Ziel: erwartete Rendite − Risikoterm − Transaktionskosten − Steuerwirkung. * Solver: CVXPY (konvexer Teil) oder MIQP für die Kardinalität.
+
Abnahmekriterien. - [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt. - [ ] Alle Kosten (Ordergebühr, Spread, Steuer) sind explizit ausgewiesen. - [ ] Backtest über 5 Jahre mit Lookahead-Selbsttest. - [ ] Vergleich gegen: nie rebalancen, jährlich rebalancen, Gleichgewichtung. - [ ] Bericht, den ein Nicht-Fachmann versteht.
Stolperfallen. Steuern sind pfadabhängig (FIFO, Freibetrag) und passen nicht sauber in ein einperiodiges Modell — Näherung wählen und die Näherung dokumentieren. Und: Wenn Ihre Strategie die Gleichgewichtung nicht schlägt, ist das ein Ergebnis, kein Misserfolg.
Ausgangslage. Für ein bestehendes Portfolio soll ein monatlicher Risikobericht entstehen.
-
Inhalte des Berichts. * VaR und CVaR auf 95 % und 99 %, historisch und Monte-Carlo-simuliert. * Risikobeiträge je Position (marginal CVaR): Wer treibt das Risiko? * Stresstests: Was passiert bei −20 % Aktienmarkt, +200 Basispunkten Zins, Korrelationsanstieg auf 0,9? * Historische Szenarien nachspielen (2008, März 2020). * Konzentrationskennzahlen (Herfindahl-Index, effektive Titelzahl).
+
Inhalte des Berichts. * VaR und CVaR auf 95 % und 99 %, historisch und Monte-Carlo-simuliert. * Risikobeiträge je Position (marginal CVaR): Wer treibt das Risiko? * Stresstests: Was passiert bei −20 % Aktienmarkt, +200 Basispunkten Zins, Korrelationsanstieg auf 0,9? * Historische Szenarien nachspielen (2008, März 2020). * Konzentrationskennzahlen (Herfindahl-Index, effektive Titelzahl).
Abnahmekriterien. - [ ] Bericht als PDF, automatisch erzeugt. - [ ] Alle Kennzahlen mit zwei unabhängigen Methoden berechnet und verglichen. - [ ] Klartext-Zusammenfassung: „Im schlechtesten Prozent der Monate verlieren Sie typischerweise X €.“
-
Stolperfallen. Die Wurzel-Zeit-Regel bei CVaR (Kapitel 20) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
+
Stolperfallen. Die Wurzel-Zeit-Regel bei CVaR (Kapitel 20) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
P9 — Wenn der Solver aussteigt: Tourenplanung in Echtgröße
Ausgangslage. P3 hat funktioniert — mit 30 Kunden. Der Auftraggeber kommt mit 400 zurück, und derselbe Code liefert nach zwanzig Minuten noch keine Lösung. Das ist keine Panne, sondern der erwartbare Umschlagpunkt.
-
Modellskizze. Zwei Verfahren am selben Problem, verglichen unter gleichem Zeitbudget: * Exakt: MILP oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap. * Heuristisch: Startlösung mit einer Faustregel, dann Verbesserung durch Simulated Annealing oder Large Neighborhood Search (Kapitel 9). * Messgröße ist nicht „wer gewinnt”, sondern ab welcher Instanzgröße sich das Blatt wendet.
+
Modellskizze. Zwei Verfahren am selben Problem, verglichen unter gleichem Zeitbudget: * Exakt: MILP oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap. * Heuristisch: Startlösung mit einer Faustregel, dann Verbesserung durch Simulated Annealing oder Large Neighborhood Search (Kapitel 9). * Messgröße ist nicht „wer gewinnt”, sondern ab welcher Instanzgröße sich das Blatt wendet.
Abnahmekriterien. - [ ] Eine Tabelle über mindestens vier Instanzgrößen: exakt gegen heuristisch, jeweils Zielwert und Laufzeit. - [ ] Der Umschlagpunkt ist auf ±50 Kunden eingegrenzt und benannt. - [ ] Bei kleinen Instanzen wird gegen die bewiesen optimale Lösung geprüft — sonst weiß niemand, wie gut die Heuristik wirklich ist. - [ ] Fester Seed, reproduzierbarer Lauf; das Zugbudget ist fix, nicht die Uhrzeit.
⚠️ Die Falle, in die hier fast jeder tappt: die Heuristik nur auf großen Instanzen zu testen, wo man das Optimum nicht kennt. Dann sieht jede Lösung gut aus.
@@ -252,7 +252,7 @@
Ausgangslage. Die Geschäftsführung will „günstiger und grüner”. Beides zugleich gibt es nicht — und die übliche Antwort, beide Ziele mit Gewichten zu verrechnen, verdeckt genau die Frage, um die es geht.
Modellskizze. Ein Transport-, Beschaffungs- oder Produktionsmodell mit zwei Zielen: * Erst beide Ziele einzeln optimieren — das gibt die Eckpunkte und damit den Rahmen. * Dann die Front über das \varepsilon-Constraint-Verfahren abfahren (Kapitel 14): ein Ziel minimieren, das andere als Nebenbedingung schrittweise verschärfen. * Ergebnis ist keine Lösung, sondern eine Kurve — plus die Angabe, was jeder eingesparte Kilogramm CO₂ an Mehrkosten bedeutet.
-
Abnahmekriterien. - [ ] Die Pareto-Front ist berechnet und gezeichnet, nicht nur beschrieben. - [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet. - [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine gewichtete Summe niemals finden würde — und warum. - [ ] Die Entscheidungsvorlage nennt drei Punkte zur Auswahl, nicht dreißig.
+
Abnahmekriterien. - [ ] Die Pareto-Front ist berechnet und gezeichnet, nicht nur beschrieben. - [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet. - [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine gewichtete Summe niemals finden würde — und warum. - [ ] Die Entscheidungsvorlage nennt drei Punkte zur Auswahl, nicht dreißig.
diff --git a/OR_HTML_04/qp-nlp.html b/OR_HTML_04/qp-nlp.html
index e74d218..ffe4d3d 100644
--- a/OR_HTML_04/qp-nlp.html
+++ b/OR_HTML_04/qp-nlp.html
@@ -107,7 +107,7 @@
Entscheidungsdiagramm: Welches Werkzeug passt zu meinem Problem? (Teil III)
-
🎯 Merksatz zum Diagramm Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die Nichtlinearität das Problem, geht es um Konvexität — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die Unsicherheit das Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
+
🎯 Merksatz zum Diagramm Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die Nichtlinearität das Problem, geht es um Konvexität — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die Unsicherheit das Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
Der häufigste Fehler an dieser Stelle ist, Unsicherheit durch Mittelwerte zu ersetzen und dann deterministisch zu rechnen. Warum das systematisch danebengeht, zeigt Kapitel 12 unter dem Namen Fluch des Durchschnitts.
Zwei weitere Kapitel schließen sich an, weil sie dieselbe Voraussetzung aufgeben, nur an einer anderen Stelle: Kapitel 14 gibt das eine Ziel auf, Kapitel 15 die Annahme, die Eingabedaten seien gegeben statt selbst geschätzt. Was dieser Teil insgesamt leistet, fasst die Synthese an seinem Ende zusammen.
@@ -116,9 +116,9 @@
📌 Kapitel auf einen Blick
-
Worum geht es? Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in Teil IV.
+
Worum geht es? Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in Teil IV.
Voraussetzungen:Kapitel 2 (Konvexität, Eigenwerte), Kapitel 5 (Dualität, Schattenpreise). Gradienten werden in Abschnitt 11.2 wiederholt.
-
Danach können Sie: Ein quadratisches Programm aufstellen, die KKT-Bedingungen anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären, eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
+
Danach können Sie: Ein quadratisches Programm aufstellen, die KKT-Bedingungen anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären, eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er negativ und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus Teil II — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
+
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er negativ und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus Teil II — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
🎯 Merksatzcp.quad_form(w, S) ist die Zeile, um die es im ganzen Kapitel geht. Sie ist genau dann harmlos, wenn \mathbf{S}positiv semidefinit ist — und genau dann ein Problem, wenn nicht. CVXPY prüft das für Sie und verweigert die Arbeit im Zweifelsfall. Diese Verweigerung ist ein Schutzmechanismus, keine Schikane.
@@ -217,13 +217,13 @@ Risiko der Mischung: 8.90 %
11.2 Lernziele
Nach diesem Kapitel können Sie …
-
… ein quadratisches Programm (QP) aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
+
… ein quadratisches Programm (QP) aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
… die KKT-Bedingungen herleiten und auf ein Optimierungsproblem anwenden.
… den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären.
… zwischen konvex und streng konvex unterscheiden und eine gültige, positiv semidefinite Kovarianzmatrix konstruieren.
Auffrischung: Gradient in einer Minute
-
Der Gradient\nabla f(\mathbf{x}) ist der Vektor aller partiellen Ableitungen:
+
Der Gradient\nabla f(\mathbf{x}) ist der Vektor aller partiellen Ableitungen:
Warum das praktisch zählt: Eine Stichproben-Kovarianzmatrix mit mehr Titeln als Beobachtungen (N > T) ist singulär — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus Kapitel 18.
+
Warum das praktisch zählt: Eine Stichproben-Kovarianzmatrix mit mehr Titeln als Beobachtungen (N > T) ist singulär — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus Kapitel 18.
Alle drei Zeilen der Tabelle lassen sich an einem winzigen Zwei-Variablen-QP direkt beobachten — inklusive der Stelle, an der CVXPY ein nicht konvexes Problem verweigert, bevor überhaupt ein Solver aufgerufen wird:
2. Primale Zulässigkeit — die ursprünglichen Bedingungen gelten: g_i(\mathbf{x}^*)\le0, h_j(\mathbf{x}^*)=0.
3. Duale Zulässigkeit — die Multiplikatoren der Ungleichungen sind nichtnegativ: \lambda_i^*\ge0.
Warum? Eine Wand kann nur drücken, nicht ziehen. Ein negatives \lambda hieße, die Bedingung würde die Lösung von sich wegziehen — dann wäre sie nicht bindend.
-
4. Komplementärer Schlupf — \lambda_i^*\cdot g_i(\mathbf{x}^*)=0 für alle i.
Bedeutung: Entweder ist die Bedingung nicht bindend (g_i<0), dann muss\lambda_i=0 sein. Oder der Multiplikator ist positiv, dann muss die Bedingung mit Gleichheit binden. Das ist derselbe Satz wie in Kapitel 5 — nur allgemeiner formuliert.
11.5 Nichtlineare Optimierung mit scipy.optimize.minimize
-
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequential Least Squares Programming).
+
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequential Least Squares Programming).
⚠️ Der entscheidende Unterschied zu CVXPY CVXPY prüft die Konvexität und garantiert bei Erfolg das globale Optimum. scipy.optimize.minimize prüft nichts und liefert ein lokales Optimum, das vom Startpunkt abhängt (siehe die Demonstration in Abschnitt 2.5). Wer SLSQP einsetzt, sollte immer mit mehreren Startpunkten rechnen und die Ergebnisse vergleichen.
Praxisfall: Entropie-maximierte Kapitalallokation
-
Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:
+
Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:
Beweisbar globales Optimum, mit entsprechendem Rechenaufwand
@@ -1219,7 +1219,7 @@ Drei Konsequenzen fuer die Praxis:
Aufgabe 11.2 ⭐ — Komplementärer Schlupf. Ein QP liefert w = (0{,}4;\ 0{,}0;\ 0{,}6) mit Bedingung w \ge 0 und Multiplikatoren \lambda = (0{,}0;\ 0{,}03;\ 0{,}0). Ist das mit KKT verträglich? Was sagt \lambda_2 = 0{,}03 wirtschaftlich?
Aufgabe 11.3 ⭐⭐ — KKT von Hand. Lösen Sie mit KKT vollständig von Hand: \min\ x_1^2 + x_2^2 \qquad\text{u. d. N.}\qquad x_1 + x_2 \ge 4 Geben Sie x^*, \lambda^* und die Interpretation von \lambda^* an. Prüfen Sie mit CVXPY.
Aufgabe 11.4 ⭐⭐ — Ungültige Kovarianzmatrix erkennen. Prüfen Sie, ob folgende Korrelationsmatrix möglich ist, und begründen Sie: \mathbf{C} = \begin{pmatrix}1{,}0&0{,}9&-0{,}9\\0{,}9&1{,}0&0{,}9\\-0{,}9&0{,}9&1{,}0\end{pmatrix} (Tipp: Wenn A stark mit B korreliert und B stark mit C, kann A dann stark negativ mit C korrelieren?)
-
Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen. Variieren Sie in Entropie_Maximierte_Allokation.py systematisch \alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\} und \beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}. Stellen Sie für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar. (a) Wie verändert \beta die Konzentration im Krypto-Titel? (b) Ab welchem \beta nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
+
Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen. Variieren Sie in Entropie_Maximierte_Allokation.py systematisch \alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\} und \beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}. Stellen Sie für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar. (a) Wie verändert \beta die Konzentration im Krypto-Titel? (b) Ab welchem \beta nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
Aufgabe 11.6 ⭐⭐⭐ — Nicht-Konvexität demonstrieren. Bauen Sie bewusst eine ungültige „Kovarianzmatrix“ (per A@A.T mit anschließend überschriebener Diagonale) und lösen Sie das Modell mit 20 Startpunkten. Dokumentieren Sie: Wie viele verschiedene Optima entstehen? Wie groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die „Portfoliovarianz“ negativ werden?
11.8 Finde den Denkfehler
@@ -1234,7 +1234,7 @@ Drei Konsequenzen fuer die Praxis:
Alle drei schwanken mit 20 %. Leerverkäufe sind in seinem Mandat erlaubt, die Gewichte dürfen also negativ werden; sie müssen sich nur zu 100 % summieren.
Sein erster Versuch mit CVXPY bricht ab:
-
DCPError: Problem does not follow DCP rules.
+
[DCPError](anhang-glossar.md#gloss:dcperror){.glossar-link}: Problem does not follow [DCP](anhang-glossar.md#gloss:dcp-disciplined-convex-programming){.glossar-link} rules.
The objective is not DCP. Its following subexpressions are not: QuadForm(...)
Er hält das für eine Einschränkung der Bibliothek und weicht auf scipy aus:
import numpy as np
@@ -1295,7 +1295,7 @@ The objective is not DCP. Its following subexpressions are not: QuadForm(...)Nicht-Konvexität kommt aus dem Alltag, nicht aus der Theorie: Mengenrabatte, Skaleneffekte, Produkte zweier Entscheidungen. Wo eines davon im Modell steht, ist die Optimalitätsgarantie weg.
Berichten Sie bei nicht-konvexen Problemen die Streuung mit, nicht die letzte Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein einzelner Wert ist es nicht.
-
Ausblick.Kapitel 12 gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
+
Ausblick.Kapitel 12 gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
Und jetzt der Punkt. Bis knapp vier Stunden ist die fünfmal teurere Gasturbine die günstigere Wahl. Erst danach hat der Kernblock seine Anfahrkosten wieder eingespielt.
Die Merit-Order — Kraftwerke nach Grenzkosten sortieren und von unten auffüllen — ist damit nur für einen einzelnen Zeitpunkt richtig. Sobald ein Tag geplant wird, hängt jede Stunde an allen anderen: Ein Block, der um 8 Uhr angefahren wird, muss bis mindestens 16 Uhr laufen, und diese Entscheidung fällt am Vorabend.
-
🎯 Merksatz Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen ein Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.
+
🎯 Merksatz Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen ein Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.
Warum funktioniert das? Der Umschlagpunkt ist eine einfache Rechnung: Die Anfahrkostendifferenz (38 500 €) geteilt durch die Grenzkostendifferenz je Stunde (83 €/MWh × 120 MW = 9 960 €/h) ergibt 3,9 Stunden. Bei zwei Kraftwerken kann man das im Kopf ausrechnen — bei fünf Blöcken und 24 Stunden nicht mehr.
@@ -162,7 +162,7 @@ Umschlagpunkt: 3.9 Stunden
… ein Unit-Commitment-Modell mit Anfahrkosten und Mindestlaufzeiten aufstellen.
… erklären, warum eine binäre erste Stufe das zweistufige Modell aus Kapitel 12 verschärft.
… zeigen, dass eine Planung auf den Erwartungswert nicht nur ungenau, sondern systematisch zu knapp ist.
-
… eine CVaR-Schranke auf eine physikalische Größe (nicht gedeckte Energie) legen.
+
… eine CVaR-Schranke auf eine physikalische Größe (nicht gedeckte Energie) legen.
… begründen, wann eine Risikoschranke etwas ändert — und wann sie überflüssig ist.
Aufgabe 17.2 ⭐ — Die fehlende Nebenbedingung. Das Modell kennt Mindestlaufzeiten, aber keine Mindeststillstandszeiten. Formulieren Sie die entsprechende Bedingung. Welchen realen Sachverhalt bildet sie ab?
Aufgabe 17.3 ⭐⭐ — Wie viele Szenarien? Rechnen Sie mit 10, 20, 40 und 80 Szenarien. Ab wann ändert sich der Commitment-Plan nicht mehr? Was folgt daraus für die Praxis — und was hat das mit Abschnitt 15.6 zu tun?
Aufgabe 17.4 ⭐⭐ — Der Wert eines Speichers. Ergänzen Sie einen Batteriespeicher (100 MW, 400 MWh, Wirkungsgrad 90 %). Um wie viel sinken die erwarteten Kosten — und was ist der Speicher damit je MWh Kapazität wert?
-
Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird. Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und Rechenzeit (Abschnitt 6.8). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus Kapitel 9 würden Sie einsetzen?
+
Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird. Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und Rechenzeit (Abschnitt 6.8). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus Kapitel 9 würden Sie einsetzen?
Bei Markowitz einsteigen. Wer Kapitel 18 überspringt, optimiert seine eigenen Schätzfehler und wundert sich über Gewichte, die niemand halten würde.
+
Bei Markowitz einsteigen. Wer Kapitel 18 überspringt, optimiert seine eigenen Schätzfehler und wundert sich über Gewichte, die niemand halten würde.
Den VaR für ein Risikomaß halten. Er sagt, ob die Schwelle gerissen wird, nicht wie schlimm es dahinter aussieht — und er ist nicht kohärent. Optimiert wird der CVaR.
Trägheit unterschätzen. Im Kraftwerkspark ist nicht die Prognosegüte das Problem, sondern dass sich ein Block mit acht Stunden Mindestlaufzeit um 18 Uhr nicht mehr herbeirufen lässt. Dieselbe Trägheit heißt im Depot Transaktionskosten.
Vier Kapitel, eine Frage: Was muss vorliegen, bevor ein Solver überhaupt sinnvoll gestartet werden kann? Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
+
Vier Kapitel, eine Frage: Was muss vorliegen, bevor ein Solver überhaupt sinnvoll gestartet werden kann? Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
Runden. Die LP-Lösung ist keine Näherung der ganzzahligen Lösung — sie kann beliebig weit danebenliegen, und gerundet sogar unzulässig werden.
Ein zu großes Big-M. Es macht das Modell nicht falsch, sondern die Suche langsam und die Schranken wertlos. So klein wie zulässig, nie „sicherheitshalber groß”.
-
Zu früh heuristisch werden. Der Umschlagpunkt, ab dem eine Metaheuristik den exakten Solver schlägt, ist eine Eigenschaft des Problems und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
+
Zu früh heuristisch werden. Der Umschlagpunkt, ab dem eine Metaheuristik den exakten Solver schlägt, ist eine Eigenschaft des Problems und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
Unsicherheit durch Mittelwerte ersetzen. Der Durchschnittskunde kauft nie. Bei asymmetrischen Kosten liegt das Optimum systematisch neben dem Mittelwert, und eine Terminzusage ist ein Quantil, kein Erwartungswert.
-
Konvexität voraussetzen, ohne sie zu prüfen. Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht von selbst.
+
Konvexität voraussetzen, ohne sie zu prüfen. Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht von selbst.
Zwei Ziele mit einem Gewicht verrechnen, bevor die Front bekannt ist. Ein Gewicht ist eine Antwort auf eine Frage, die noch niemand gestellt hat. Die Pareto-Front stellt sie zuerst.
Wenn Sie nur eines mitnehmen
-
🎯 Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet Zustandsraum, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, welche Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
+
🎯 Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet Zustandsraum, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, welche Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
🚀 In 5 Minuten gelöst: drei Zeilen, die einen echten Fehler fangen
-
Die Schreinerei aus Kapitel 1, nur mit etwas knapperen Kapazitäten. Der Solver liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
+
Die Schreinerei aus Kapitel 1, nur mit etwas knapperen Kapazitäten. Der Solver liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
import numpy as np
-from scipy.optimize import linprog
+from scipy.optimize import [linprog](anhang-glossar.md#gloss:linprog){.glossar-link}verbrauch_je_stueck = np.array([[3.0, 1.0], # Montagestunden je Tisch/Stuhl [6.0, 1.0]]) # Plattenmaterial je Tisch/Stuhl
@@ -145,7 +145,7 @@
AssertionError: Plan [31. 48.] braucht [141. 234.], verfuegbar sind [140. 232.]
-
Und jetzt der Punkt. Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des LP. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
+
Und jetzt der Punkt. Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des LP. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
Bemerkenswert ist, was der Test prüft. Er prüft nicht, ob 30,67 das Optimum ist — das könnte er gar nicht, dafür bräuchte er einen zweiten Solver. Er prüft die Eigenschaft, die jede brauchbare Lösung haben muss: Sie hält die Kapazität ein. Diese Verschiebung — von der Frage nach dem richtigen Wert zur Frage nach den notwendigen Eigenschaften — ist der Schlüssel zum ganzen Kapitel.
🎯 Merksatz Ein Optimierungsmodell lässt sich fast nie gegen den richtigen Wert testen. Gegen seine Eigenschaften lässt es sich immer testen.
@@ -536,7 +536,7 @@ AssertionError: Plan [31. 48.] braucht [141. 234.], verfuegbar sind [140. 232.]<
@pytest.fixture(params=MODELLBAUER, ids=NAMEN)
-
Jeder Test läuft zweimal — einmal mit GLOP, einmal mit SciPy. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell
+
Jeder Test läuft zweimal — einmal mit GLOP, einmal mit SciPy. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell
test_beide_solver_liefern_dasselbe
@@ -1127,11 +1127,11 @@ NICHT ablesen darf man:
(Die genauen Prozentwerte schwanken von Lauf zu Lauf — es sind Verhältnisse zweier Zeitmessungen. Die Größenordnungen sind stabil.)
-
Bei OR-Tools gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren Solver sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau (Kapitel 3).
-
Die zweite unbequeme Zahl steht ganz rechts: CVXPY braucht rund 240 MB, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
+
Bei OR-Tools gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren Solver sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau (Kapitel 3).
+
Die zweite unbequeme Zahl steht ganz rechts: CVXPY braucht rund 240 MB, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
⚠️ Was diese Tabelle nicht sagt
-
Sie sagt nicht „ortools ist langsamer als scipy”. Gemessen wurde ein Problemtyp in einer Formulierung auf einer Maschine. Ein MILP statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
+
Sie sagt nicht „ortools ist langsamer als scipy”. Gemessen wurde ein Problemtyp in einer Formulierung auf einer Maschine. Ein MILP statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
Übertragbar ist der Trend, nicht der Absolutwert: dass der Aufbauanteil mit der Größe wächst, dass CVXPY einen konstanten Speichersockel hat. Der Zweck des Programms ist, dass Sie es auf Ihrer Maschine mit Ihrem Problem laufen lassen.
@@ -1560,12 +1560,12 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
================================================================================
💻 Code-Durchgang: drei Entscheidungen
-
1. Das Domänenmodell ist das API-Schema. Der Parameter heißt problem: Produktionsproblem — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus Kapitel 1 und antwortet bei Unsinn mit 422, bevor ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die or_kern.py schon beim Einlesen abfängt.
-
2. Die Antwort enthält Status und Gap, nicht nur Zahlen. Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das Loesung-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe DTO, das in Abschnitt 22.7 schon eine Prozessgrenze überlebt hat.
+
1. Das Domänenmodell ist das API-Schema. Der Parameter heißt problem: Produktionsproblem — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus Kapitel 1 und antwortet bei Unsinn mit 422, bevor ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die or_kern.py schon beim Einlesen abfängt.
+
2. Die Antwort enthält Status und Gap, nicht nur Zahlen. Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das Loesung-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe DTO, das in Abschnitt 22.7 schon eine Prozessgrenze überlebt hat.
3. Die Abnahmeprüfung läuft auch hier.pruefe_loesung() steht im Arbeiter, nicht nur im Skript. Ein Dienst ohne sie liefert Fehler schneller aus, als ein Mensch sie finden könnte.
Threads oder Prozesse? Eine Messung, keine Meinung
-
Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL während Solve() frei. Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
+
Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL während Solve() frei. Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
Das gilt aber nicht allgemein. Eine in reinem Python geschriebene Heuristik (Kapitel 9) hält den GIL die ganze Zeit — für sie braucht derselbe Dienst einen ProcessPoolExecutor. Die Entscheidung gehört gemessen, nicht angenommen.
Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
+
Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
Voraussetzungen:Kapitel 5, Kapitel 11. Grundbegriffe der Statistik (Erwartungswert, Quantil).
Danach können Sie: Ein zweistufiges stochastisches Modell aufstellen, Monte-Carlo zur Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht.
🎯 Merksatz Die optimale Entscheidung unter Unsicherheit ist fast nie die Entscheidung, die für den Mittelwert optimal wäre. Sie hängt davon ab, welcher der beiden Fehler teurer ist — und verschiebt sich zu der Seite, auf der Irren billiger ist. Wer mit dem Mittelwert plant, hat diese Frage nie gestellt.
-
Dieses Muster heißt Newsvendor-Problem (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
+
Dieses Muster heißt Newsvendor-Problem (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
… den Fluch des Durchschnitts (Flaw of Averages) an einem Beispiel erklären.
… ein zweistufiges stochastisches Programm mit Recourse formulieren.
… Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
-
… ein robustes Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
-
… eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
+
… ein robustes Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
+
… eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
… begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
… das kritische Verhältnis eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen.
… eine Terminzusage als Quantil formulieren statt als Mittelwert.
@@ -343,7 +343,7 @@ Kosten fuer ausgewaehlte x zum Vergleich:
Das Optimum liegt exakt auf einem Szenariowert (250 = 'Volatil'),
nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.
Kein Rechentrick, sondern brute-force über alle ganzzahligen x von 0 bis 500 — das Optimum landet exakt dort, wo es die Handrechnung vorhersagt.
-
Warum? Die Kostenfunktion ist asymmetrisch: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die Jensensche Ungleichung: Für konvexe Kostenfunktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]) — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
+
Warum? Die Kostenfunktion ist asymmetrisch: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die Jensensche Ungleichung: Für konvexe Kostenfunktionen gilt \mathbb{E}[f(X)] \ge f(\mathbb{E}[X]) — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
🎯 Merksatz Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt.
@@ -364,8 +364,8 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.<
@@ -719,7 +719,7 @@ den Mittelwert (225), weil Unterdeckung (120 EUR)
deutlich teurer ist als Leerlauf (5 EUR).
==================================================================================
Lesen Sie die Vergleichstabelle von unten nach oben. Die beiden extremen Haltungen sind die teuersten: Wer den Worst Case abdeckt (500 Einheiten), zahlt 5 000 € zu viel für Kapazität, die in 80 % der Fälle brachliegt. Wer optimistisch plant (100), zahlt 2 625 € Strafe für ständige Notzukäufe. Die naive Mittelwertplanung liegt dazwischen — aber eben auch nicht optimal.
-
Der EVPI von 7 375 € ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe EVPI interpretieren (Abschnitt 12.8).
+
Der EVPI von 7 375 € ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe EVPI interpretieren (Abschnitt 12.8).
💡 Was ist der EVPI? Der Expected Value of Perfect Information beziffert, wie viel eine perfekte Prognose wert wäre: die Differenz zwischen den Kosten unter Unsicherheit und den Kosten bei vollständigem Wissen. Er ist eine Obergrenze für jedes Prognoseprojekt. Wenn der EVPI bei 12 000 € pro Jahr liegt, lohnt sich keine Prognosesoftware für 50 000 € — selbst wenn sie perfekt wäre. Diese Zahl bewahrt Projekte vor teuren Fehlinvestitionen.
@@ -840,7 +840,7 @@ deutlich teurer ist als Leerlauf (5 EUR).
print(" als sie im Normalfall kostet.")print("="*88)
-
⚠️ Der Preis der Robustheit Robuste Modelle sind konservativ. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. Unsicherheitsmenge realistisch wählen. Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. Budgeted Uncertainty (Bertsimas/Sim): Man nimmt an, dass höchstens \Gamma von n Parametern gleichzeitig ihren Worst Case annehmen. \Gamma steuert die Vorsicht stufenlos. 3. Nutzen und Preis immer beziffern — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.
+
⚠️ Der Preis der Robustheit Robuste Modelle sind konservativ. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. Unsicherheitsmenge realistisch wählen. Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. Budgeted Uncertainty (Bertsimas/Sim): Man nimmt an, dass höchstens \Gamma von n Parametern gleichzeitig ihren Worst Case annehmen. \Gamma steuert die Vorsicht stufenlos. 3. Nutzen und Preis immer beziffern — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.
@@ -853,7 +853,7 @@ deutlich teurer ist als Leerlauf (5 EUR).
📐 Formel-Lesehilfe * \mathbf{a} — der unsichere Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist keine Zahl, sondern eine Zufallsgröße. * \mathbf{x} — Ihre Entscheidung. Sie fällt, bevor\mathbf{a} sich zeigt. * \alpha — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. \alpha = 0{,}05 heißt „in höchstens 5 % der Fälle darf es schiefgehen“.
Ohne Formel gesagt: „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“
-
Das Problem daran. So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
+
Das Problem daran. So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
Weg 1: Normalverteilung — die Bedingung wird ein Kegel
Ist \mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma}), dann ist \mathbf{a}^\top\mathbf{x} für festes \mathbf{x} selbst normalverteilt, mit Erwartungswert \hat{\mathbf{a}}^\top\mathbf{x} und Standardabweichung \sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}. Damit lässt sich die Wahrscheinlichkeit durch ein Quantil ersetzen:
@@ -863,9 +863,9 @@ deutlich teurer ist als Leerlauf (5 EUR).
\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad
z_{1-\alpha} = \Phi^{-1}(1-\alpha)
-
Das ist eine Second-Order-Cone-Bedingung — dieselbe Bauform, die Kapitel 11 eingeführt hat, und CVXPY löst sie ohne Umstände.
+
Das ist eine Second-Order-Cone-Bedingung — dieselbe Bauform, die Kapitel 11 eingeführt hat, und CVXPY löst sie ohne Umstände.
-
🎯 Warum eine Norm und keine Summe Der Sicherheitszuschlag \lVert\mathbf{L}^\top\mathbf{x}\rVert_2 ist keine Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken gemeinsam weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen.
+
🎯 Warum eine Norm und keine Summe Der Sicherheitszuschlag \lVert\mathbf{L}^\top\mathbf{x}\rVert_2 ist keine Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken gemeinsam weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen.
Weg 2: Szenarien — Big-M ohne Verteilungsannahme
Liegen statt einer Verteilung nur S beobachtete Szenarien vor, bekommt jedes eine Binärvariable z_s, die sagt, ob es verletzt werden darf:
Aus dem Kegelproblem wird ein MILP mit S Binärvariablen. Für M gilt, was Kapitel 6 über die Big-M-Falle sagt: so klein wie möglich. Hier ist M = b die kleinste gültige Schranke, denn \mathbf{a}_s^\top\mathbf{x} \ge 0 — größer kann eine Verletzung gar nicht ausfallen.
+
Aus dem Kegelproblem wird ein MILP mit S Binärvariablen. Für M gilt, was Kapitel 6 über die Big-M-Falle sagt: so klein wie möglich. Hier ist M = b die kleinste gültige Schranke, denn \mathbf{a}_s^\top\mathbf{x} \ge 0 — größer kann eine Verletzung gar nicht ausfallen.
Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus. Das ist kein Fehler, sondern die Definition des Erwartungswerts — und dieselbe Falle wie in Abschnitt 12.3, nur teurer. Er kauft die billigsten Quellen je erwarteter Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, Wind und Sonne schrumpfen auf einen Rest.
Zweitens: Sicherheit ist konvex bepreist. Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das 4,5-fache. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der nächste Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.
-
Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung. Der Plan verspricht 95 % und hält 87,4 %. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht alles zugleich ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
+
Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung. Der Plan verspricht 95 % und hält 87,4 %. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht alles zugleich ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf 94,8 %, kostet dafür 16,7 % mehr — und kauft keine Windkraft mehr. Die Logik dahinter ist hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich Bei S Szenarien und \alpha = 5\,\% darf das Modell genau 0{,}05 \cdot S davon ignorieren — und es sucht sich die teuersten aus. In der Stichprobe trifft es seine Quote dadurch immer; außerhalb streut sie.
Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei S = 200, 400, 800, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen 93,1 % und 96,7 %. Die Spanne wurde von S = 200 auf 400 enger (3,5 → 1,2 Prozentpunkte), bei S = 800 aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht.
-
Konsequenz für die Praxis: Eine mit Szenarien erkaufte Zusage immer an einer zurückgehaltenen Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in Kapitel 21.
+
Konsequenz für die Praxis: Eine mit Szenarien erkaufte Zusage immer an einer zurückgehaltenen Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in Kapitel 21.
🎯 Merksatz Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern.
Aufgabe 12.2 ⭐ — Ansatz wählen. Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? (e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält?
Aufgabe 12.3 ⭐⭐ — Zweistufiges Modell rechnen. Wiederholen Sie die Handrechnung Warum der Mittelwert in die Irre führt mit geänderten Kosten: Spot 60 € statt 120 €. (a) Wie ändert sich die optimale Kapazität? (b) Erklären Sie die Richtung der Änderung. (c) Bei welchem Spot-Preis wäre die Mittelwert-Planung optimal?
Aufgabe 12.4 ⭐⭐ — EVPI interpretieren. Ihr Modell liefert EVPI = 8 400 € pro Jahr. Ein Anbieter verlangt 15 000 € jährlich für eine Prognoselösung, die „80 % Treffsicherheit“ verspricht. Wie argumentieren Sie?
-
Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern. Erweitern Sie Monte_Carlo.py: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
+
Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern. Erweitern Sie Monte_Carlo.py: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen.Chance_Constraints.py beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das szenariobasierte Modell meldet dann bei 95 % infeasible, das analytische dagegen optimal. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten.
Aufgabe 12.7 ⭐⭐⭐ — Budgeted Uncertainty. Erweitern Sie Robuste_Optimierung.py um den Ansatz von Bertsimas/Sim: Höchstens \Gamma Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie \Gamma \in \{0, 1, 2, 3, 4\} und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei \Gamma ganzzahlig genügt es, die \Gamma größten \delta_i w_i abzuziehen — das lässt sich in CVXPY mit cp.sum_largest formulieren.)
Genau eine Antwort ist jeweils richtig. Auflösung in Anhang A.
1. Bei einem Ersatzteil kostet ein Stück zu wenig 1 400 €, ein Stück zu viel 120 €. Der Bedarf beträgt im Mittel 30 Stück. Wie viel bestellen Sie? (a) 30 — das ist der Erwartungswert des Bedarfs. (b) Deutlich mehr als 30, nämlich das 92,1-%-Quantil des Bedarfs. Das kritische Verhältnis 1400/(1400+120) sagt, wie weit man sich auf die günstigere Fehlerseite stellen soll. (c) Weniger als 30, weil Lagerhaltung Kapital bindet.
2. Was unterscheidet stochastische** von robuster Optimierung?** (a) Stochastische Optimierung ist genauer, robuste ist eine Näherung für schnelle Rechnungen. (b) Stochastische Optimierung braucht Wahrscheinlichkeiten und optimiert den Erwartungswert; robuste Optimierung braucht nur Bandbreiten und sichert den schlechtesten Fall darin ab. (c) Robuste Optimierung berücksichtigt mehr Szenarien.
-
3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch? (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen linear sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
+
3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch? (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen linear sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
Eine Zusage gilt nur für die unterstellte Verteilung. Ein Plan, der unter Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle nicht kannte, in der alles zugleich ausfällt. Szenariobasierte Zusagen wiederum überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden.
Beziffern Sie immer Preis und Nutzen der Absicherung. Ohne beide Zahlen ist die Entscheidung nicht begründbar.
-
Ausblick.Kapitel 13 fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
+
Ausblick.Kapitel 13 fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb, Technik und Finanzmärkte
-
Ein praxisorientiertes Kompendium für Entscheider mit Constraint Programming, Vektor- und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
+
Ein praxisorientiertes Kompendium für Entscheider mit Constraint Programming, Vektor- und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
Mit Python planen, optimieren und entscheiden: von Personal, Schichten und Logistik über Energie, Ressourcen und Netzwerke bis zur Portfoliooptimierung an den Finanzmärkten.
Autor / Herausgeber: Dieter Schlüter
<dieter(dot)schlueter(atsign)linix(dot)de>
-
Stand: 10. September 2026 v16.11
+
Stand: 10. September 2026 v17.39
Über den Kurs
Vorwort
@@ -125,9 +125,9 @@
Die Programme des Kurses laufen unter Linux, MacOS und Windows (WSL) gleichermaßen, entweder im Terminal, in Jupyter Notebook oder nach dem Hochladen in Google Colab. Entwickelt und ausgiebig getestet wurden sie in der Linux-Umgebung.
Ziel der Masterclass
-
Ein umfassendes, didaktisch von Grund auf aufgebautes Lehrbuch zur mathematischen Entscheidungsoptimierung (Operations Research, OR) mit praktischer Umsetzung in Python. Dieser Kurs der mathematischen Optimierung setzt kein Vorwissen in OR voraus und führt schrittweise von den algebraischen und kombinatorischen Grundlagen über Scheduling- und Constraint-Modelle bis hin zur professionellen Anwendung im quantitativen Aktienhandel, Portfoliomanagement und Risikocontrolling.
+
Ein umfassendes, didaktisch von Grund auf aufgebautes Lehrbuch zur mathematischen Entscheidungsoptimierung (Operations Research, OR) mit praktischer Umsetzung in Python. Dieser Kurs der mathematischen Optimierung setzt kein Vorwissen in OR voraus und führt schrittweise von den algebraischen und kombinatorischen Grundlagen über Scheduling- und Constraint-Modelle bis hin zur professionellen Anwendung im quantitativen Aktienhandel, Portfoliomanagement und Risikocontrolling.
Vorausgesetzte Kenntnisse
-
Vorausgesetzt werden sichere Python-Kenntnisse (Funktionen, Klassen, NumPy, pandas) sowie Mathematik auf Grundstudiumsniveau:
+
Vorausgesetzt werden sichere Python-Kenntnisse (Funktionen, Klassen, NumPy, pandas) sowie Mathematik auf Grundstudiumsniveau:
@@ -149,7 +149,7 @@
Analysis
-
Ableitung, Gradient, notwendige Bedingung erster Ordnung
+
Ableitung, Gradient, notwendige Bedingung erster Ordnung
Prüfen Sie mit diesem Skript, ob alles bereitsteht, bevor Sie mit Kapitel 1 beginnen. Es meldet für jedes Paket Version und Status und löst ein Mini-Optimierungsproblem.
-
Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: highspy und cvxpy werden in der Paketübersicht nur auf Anwesenheit geprüft, und ortools lädt seine native Bibliothek zuerst. Der Grund: ortools und highspy bringen jeweils eine eigene HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit. Lädt deren HiGHS-Kopie zuerst, bricht der Test beim CP-SAT-Funktionstest mit einem kryptischen ImportError: undefined symbol ab; so gewinnt die Kopie von ortools, und CVXPY verzichtet lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und Abhilfen: Abschnitt 3.5 und Anhang C.
+
Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: highspy und cvxpy werden in der Paketübersicht nur auf Anwesenheit geprüft, und ortools lädt seine native Bibliothek zuerst. Der Grund: ortools und highspy bringen jeweils eine eigene HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und cvxpy importiert ein installiertes highspy bei der Solver-Erkennung selbst mit. Lädt deren HiGHS-Kopie zuerst, bricht der Test beim CP-SAT-Funktionstest mit einem kryptischen ImportError: undefined symbol ab; so gewinnt die Kopie von ortools, und CVXPY verzichtet lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und Abhilfen: Abschnitt 3.5 und Anhang C.
#!/usr/bin/env python3# Installationstest.py
@@ -364,20 +364,20 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
Was Planen mit Durchschnitten kostet, wird hier beziffert, nicht behauptet. Im Kraftwerkseinsatz aus Kapitel 17 kostet ein Plan, der mit dem Wind-Erwartungswert rechnet, in der Wirklichkeit 149 % mehr als einer, der 40 Szenarien berücksichtigt — 874 870 € statt 351 356 € — und führt in 28 von 40 Szenarien zum Lastabwurf statt in keinem.
„Wie sicher soll es sein?“ wird eine Preisfrage.Abschnitt 12.7 rechnet aus, was ein Prozentpunkt Versorgungssicherheit kostet: rund 56 000 € auf dem Weg zu 80 %, rund 254 000 € zwischen 95 und 99 %. Das 4,5-fache. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.
-
Für die Sitzung gibt es einen Bericht, keine Tabelle.Abschnitt 22.4 beziffert je Nebenbedingung, was sie den Plan kostet, und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — „warum ausgerechnet ich?“ — liefert Kapitel 22 den Constraint-Trace, der die einzelne Zuweisung begründet.
+
Für die Sitzung gibt es einen Bericht, keine Tabelle.Abschnitt 22.4 beziffert je Nebenbedingung, was sie den Plan kostet, und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — „warum ausgerechnet ich?“ — liefert Kapitel 22 den Constraint-Trace, der die einzelne Zuweisung begründet.
Ihr Weg. Zuerst Pfad E (2–4 Stunden): Abschnitt 1.5 und Anhang B. Wenn Sie danach wissen wollen, was in Ihrem Betrieb geht, Pfad B und Projekt P1 oder P2.
Woran es scheitert. Nicht am Modell, sondern an der Einführung — das ist der meistgenannte Befund in Kapitel 22. Und an einer Verwechslung, die dieses Buch Ihnen nicht abnehmen kann: Ein Bericht erklärt das Modell, nicht die Wirklichkeit. Ist ein Deckungsbeitrag falsch geschätzt, ist er überzeugend und falsch. Fragen Sie deshalb jedes Mal, worauf eine Zahl beruht.
-
Eine Erwartung, die dieses Buch enttäuscht: Es nennt keine ROI-Kennzahl und keine Amortisationsdauer. Beide hängen an Ihrem Betrieb, und jede Zahl hier wäre erfunden. Was es stattdessen liefert, ist das Werkzeug, Ihre eigene auszurechnen: den EVPI in Kapitel 12 (was eine perfekte Prognose überhaupt wert wäre — eine Obergrenze für jedes Prognoseprojekt) und die Kostenzurechnung aus Abschnitt 22.4.
+
Eine Erwartung, die dieses Buch enttäuscht: Es nennt keine ROI-Kennzahl und keine Amortisationsdauer. Beide hängen an Ihrem Betrieb, und jede Zahl hier wäre erfunden. Was es stattdessen liefert, ist das Werkzeug, Ihre eigene auszurechnen: den EVPI in Kapitel 12 (was eine perfekte Prognose überhaupt wert wäre — eine Obergrenze für jedes Prognoseprojekt) und die Kostenzurechnung aus Abschnitt 22.4.
Für Ingenieure und Produktionsplaner — die Physik des Systems
„Der Engpass wandert. Kaum ist eine Maschine entlastet, steht die nächste — und niemand kann mir vorher sagen, welche.“
-
Der Engpass lässt sich benennen und bepreisen.Abschnitt 22.4 rechnet für jede Kapazität aus, was die nächste Stunde wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis der Lackiererei exakt bis +15 Stunden; eine ganze Sonderschicht von 30 Stunden bringt deshalb 60 € statt der rechnerischen 120 €. Die zweite Hälfte wäre bezahlt und wirkungslos.
-
Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.Kapitel 7 modelliert Maschinenbelegung mit Intervallvariablen, Kapitel 9 zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten Rüstzeit, CP-SAT allein 2 628, Simulated Annealing 2 343, Annealing plus LNS 2 289 — und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
+
Der Engpass lässt sich benennen und bepreisen.Abschnitt 22.4 rechnet für jede Kapazität aus, was die nächste Stunde wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis der Lackiererei exakt bis +15 Stunden; eine ganze Sonderschicht von 30 Stunden bringt deshalb 60 € statt der rechnerischen 120 €. Die zweite Hälfte wäre bezahlt und wirkungslos.
+
Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.Kapitel 7 modelliert Maschinenbelegung mit Intervallvariablen, Kapitel 9 zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten Rüstzeit, CP-SAT allein 2 628, Simulated Annealing 2 343, Annealing plus LNS2 289 — und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
Wenn der Solver zu lange braucht, gibt es Zahlen statt Ratschläge.Abschnitt 7.7 misst, was mehrere Suchstränge bringen (hier Faktor 12,3) — und ab wo mehr Arbeiter wieder schaden.
Ihr Weg. Pfad B bis Kapitel 7, dann Projekt P5 (Produktionsplanung mit Rüstkosten). Wenn Ihre echten Instanzen den Solver stehen lassen: Kapitel 9 und Projekt P9.
@@ -387,18 +387,18 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
„Die Optimierung liefert mir Gewichte, die kein Mensch halten würde: 60 % in einem Titel, null in fünf anderen. Und im nächsten Quartal sieht sie völlig anders aus.“
-
Der Grund dafür steht in Kapitel 18, nicht in Kapitel 19. Eine aus kurzen Zeitreihen geschätzte Kovarianzmatrix ist schlecht konditioniert; der Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel; ihre Wirkung wird an Eigenwertspektrum und Konditionszahl gemessen, nicht behauptet.
-
Der VaR verschweigt genau das, worauf es ankommt. In der Stichprobe aus Kapitel 20 sagt der VaR: „an 95 % der Tage höchstens 1,86 % Verlust“. Der CVaR sagt: „und wenn doch, dann im Mittel 2,99 %“. Der schlechteste Tag der Stichprobe liegt bei −23,0 % — am VaR ändert dieser eine Tag nichts, am CVaR sehr wohl. Deshalb ist der CVaR die Größe, die man optimiert.
-
Transaktionskosten und Rebalancing sind Teil des Modells, nicht ein Abzug danach.Kapitel 20 nimmt sie in die Zielfunktion, Kapitel 21 setzt den Backtest daneben — samt der Fallen, die einen guten Backtest wertlos machen.
+
Der Grund dafür steht in Kapitel 18, nicht in Kapitel 19. Eine aus kurzen Zeitreihen geschätzte Kovarianzmatrix ist schlecht konditioniert; der Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel; ihre Wirkung wird an Eigenwertspektrum und Konditionszahl gemessen, nicht behauptet.
+
Der VaR verschweigt genau das, worauf es ankommt. In der Stichprobe aus Kapitel 20 sagt der VaR: „an 95 % der Tage höchstens 1,86 % Verlust“. Der CVaR sagt: „und wenn doch, dann im Mittel 2,99 %“. Der schlechteste Tag der Stichprobe liegt bei −23,0 % — am VaR ändert dieser eine Tag nichts, am CVaR sehr wohl. Deshalb ist der CVaR die Größe, die man optimiert.
Ihr Weg. Pfad D, und zwar mitKapitel 18. Danach Projekt P6 (Portfolio-Rebalancer) oder P7 (Risikoreport mit CVaR und Stresstests).
-
Woran es scheitert. An der Reihenfolge. Wer bei Markowitz einsteigt und die Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler maximiert — und merkt es erst im Echtgeldbetrieb.
+
Woran es scheitert. An der Reihenfolge. Wer bei Markowitz einsteigt und die Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler maximiert — und merkt es erst im Echtgeldbetrieb.
Für Studierende und Data Scientists — die Methodik
„Ich kann ein Modell trainieren und den Fehler senken. Aber wie wird aus einer Prognose eine Entscheidung — und woher weiß ich, dass sie gut ist?“
-
Die unbequemste Erkenntnis zuerst.Kapitel 15 zeigt an einem gerechneten Fall, dass das Modell mit dem schlechteren MSE die günstigere Entscheidung trifft. Prognosegüte und Entscheidungsqualität sind zwei verschiedene Größen; wer die eine optimiert, bekommt die andere nicht geschenkt.
+
Die unbequemste Erkenntnis zuerst.Kapitel 15 zeigt an einem gerechneten Fall, dass das Modell mit dem schlechterenMSE die günstigere Entscheidung trifft. Prognosegüte und Entscheidungsqualität sind zwei verschiedene Größen; wer die eine optimiert, bekommt die andere nicht geschenkt.
Dieselbe Mathematik, zwei Welten.Kapitel 16 lässt denselben Code einmal über eine Werkstatt und einmal über ein Depot laufen — und benennt die drei Stellen, an denen die Analogie endet. Struktur zu erkennen ist die eigentliche Fähigkeit, nicht Bibliothekskenntnis; zu wissen, wo eine Analogie aufhört, gehört dazu.
Es wird gerechnet, nicht nacherzählt. Zu jedem Kapitel gehören Aufgaben mit vollständigen Lösungen, Handrechnungen und ein „Finde den Denkfehler“ — und jedes abgedruckte Ergebnis stammt aus einem echten Lauf des danebenstehenden Programms.
@@ -410,13 +410,13 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
Pfad A — Vollständiger Lehrgang (100–140 Stunden)
Kapitel 1 bis Kapitel 23 in Reihenfolge, danach ein Projekt aus der Projektwerkstatt. Empfohlen, wenn Sie OR systematisch lernen wollen. Rechnen Sie mit 4–6 Stunden je Kapitel inklusive Übungen.
Pfad B — Planung, Disposition, Personaleinsatz (ca. 25 Stunden)
-
Kapitel 1 (Bausteine) → Kapitel 2 (bis Abschnitt 2.5) → Kapitel 4 (vom Wunsch zum Modell) → Kapitel 5 (LP, Schattenpreise) → Kapitel 6 (Ja/Nein-Entscheidungen) → Kapitel 7 (CP-SAT, Scheduling) → Projekt P1 (Vertretungsplaner) oder P2 (Schichtplanung). Das ist der kürzeste Weg zu einem einsetzbaren Dienstplan-Optimierer.
+
Kapitel 1 (Bausteine) → Kapitel 2 (bis Abschnitt 2.5) → Kapitel 4 (vom Wunsch zum Modell) → Kapitel 5 (LP, Schattenpreise) → Kapitel 6 (Ja/Nein-Entscheidungen) → Kapitel 7 (CP-SAT, Scheduling) → Projekt P1 (Vertretungsplaner) oder P2 (Schichtplanung). Das ist der kürzeste Weg zu einem einsetzbaren Dienstplan-Optimierer.
Wenn der Solver bei Ihrer echten Instanz stehen bleibt:Kapitel 9 und Projekt P9.
Pfad C — Logistik und Tourenplanung (ca. 25 Stunden)
Wenn die Instanzen zu groß werden:Kapitel 9 (gute Lösung in fester Zeit) und Kapitel 10 (das Modell umbauen statt die Lösung raten) — dazu Projekt P9, das beide Wege am selben Problem vergleicht.
Pfad D — Quantitative Finance (ca. 30 Stunden)
-
Kapitel 1 → Kapitel 2 → Kapitel 11 (QP, KKT) → Kapitel 18 (Daten, Shrinkage) → Kapitel 19 (Markowitz) → Kapitel 20 (CVaR) → Kapitel 21 (Backtest) → Projekt P6 (Portfolio-Rebalancer) oder P7 (Risikoreport). Wichtig: Überspringen Sie Kapitel 18 nicht. Wer direkt bei Markowitz einsteigt, optimiert Schätzrauschen und wundert sich über absurde Gewichte.
+
Kapitel 1 → Kapitel 2 → Kapitel 11 (QP, KKT) → Kapitel 18 (Daten, Shrinkage) → Kapitel 19 (Markowitz) → Kapitel 20 (CVaR) → Kapitel 21 (Backtest) → Projekt P6 (Portfolio-Rebalancer) oder P7 (Risikoreport). Wichtig: Überspringen Sie Kapitel 18 nicht. Wer direkt bei Markowitz einsteigt, optimiert Schätzrauschen und wundert sich über absurde Gewichte.
Pfad E — Ich habe morgen ein konkretes Problem (2–4 Stunden)
Lesen Sie Abschnitt 1.5 (die vier Bausteine) und Abschnitt 4.6 (welcher Satz welcher Baustein ist), dann Anhang B (Modellierungsmuster) und suchen Sie dort das Muster, das zu Ihrem Problem passt. Von jedem Muster führt ein Verweis in das zuständige Kapitel. Wie die gewählte Bibliothek es schreibt, steht in Anhang D (Spickzettel); wenn etwas nicht läuft, in Anhang C (Fehlerdiagnose).
Pfad F — Vom Prototyp in den Betrieb (ca. 15 Stunden)
@@ -451,7 +451,7 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen.
Es ist keine Anleitung zum Bau eigener Solver — wir nutzen ausgereifte Implementierungen und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der Simplex-Algorithmus in Kapitel 5, den wir von Grund auf programmieren, weil man ihn verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
+
Es ist keine Anleitung zum Bau eigener Solver — wir nutzen ausgereifte Implementierungen und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der Simplex-Algorithmus in Kapitel 5, den wir von Grund auf programmieren, weil man ihn verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
Es ist kein Buch über maschinelles Lernen. Prognosen kommen nur so weit vor, wie sie als Eingangsdaten einer Optimierung nötig sind.
Und der vierte Teil ist ausdrücklich keine Anlageberatung. Die Finanzbeispiele demonstrieren Methodik an realen Daten, nicht handelbare Strategien. Kapitel 22 erklärt ausführlich, warum ein gut aussehender Backtest noch keine funktionierende Strategie ist.
diff --git a/Operations_Research_mit_Python_Version_04.md b/Operations_Research_mit_Python_Version_04.md
index b117c2e..16d0f39 100644
--- a/Operations_Research_mit_Python_Version_04.md
+++ b/Operations_Research_mit_Python_Version_04.md
@@ -18,7 +18,7 @@
## Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb, Technik und Finanzmärkte {.unlisted .unnumbered}
-**Ein praxisorientiertes Kompendium für Entscheider** mit Constraint Programming, Vektor- und Matrixmethoden sowie HiGHS`\index{HiGHS}`{=latex}, SciPy, Google OR-Tools`\index{OR-Tools}`{=latex} und CVXPY.
+**Ein praxisorientiertes Kompendium für Entscheider** mit Constraint Programming, Vektor- und Matrixmethoden sowie [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex}, [SciPy](#gloss:scipy){.glossar-link}, Google [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} und [CVXPY](#gloss:cvxpy){.glossar-link}.
Mit Python planen, optimieren und entscheiden: von **Personal, Schichten und Logistik** über **Energie, Ressourcen und Netzwerke** bis zur **Portfoliooptimierung an den Finanzmärkten**.
@@ -26,7 +26,7 @@ Autor / Herausgeber: Dieter Schlüter
-Stand: 10. September 2026 v16.09
+Stand: 10. September 2026 v17.38
---
@@ -62,16 +62,16 @@ Die Programme des Kurses laufen unter Linux, MacOS und Windows (WSL) gleicherma
## Ziel der Masterclass {-}
-Ein umfassendes, didaktisch von Grund auf aufgebautes Lehrbuch zur mathematischen Entscheidungsoptimierung (**Operations Research, OR**) mit praktischer Umsetzung in **Python**. Dieser Kurs der **mathematischen Optimierung** setzt kein Vorwissen in OR voraus und führt schrittweise von den algebraischen und kombinatorischen Grundlagen über Scheduling- und Constraint-Modelle bis hin zur professionellen Anwendung im quantitativen Aktienhandel, Portfoliomanagement und Risikocontrolling.
+Ein umfassendes, didaktisch von Grund auf aufgebautes Lehrbuch zur mathematischen Entscheidungsoptimierung (**Operations Research, OR**) mit praktischer Umsetzung in **Python**. Dieser Kurs der **mathematischen Optimierung** setzt kein Vorwissen in OR voraus und führt schrittweise von den algebraischen und kombinatorischen Grundlagen über [Scheduling](#gloss:scheduling){.glossar-link}- und Constraint-Modelle bis hin zur professionellen Anwendung im quantitativen Aktienhandel, Portfoliomanagement und Risikocontrolling.
### Vorausgesetzte Kenntnisse {-}
-Vorausgesetzt werden **sichere Python-Kenntnisse** (Funktionen, Klassen, NumPy, pandas) sowie Mathematik auf Grundstudiumsniveau:
+Vorausgesetzt werden **sichere Python-Kenntnisse** (Funktionen, Klassen, [NumPy](#gloss:numpy){.glossar-link}, [pandas](#gloss:pandas){.glossar-link}) sowie Mathematik auf Grundstudiumsniveau:
| Gebiet | Was Sie können sollten | Wo im Buch gebraucht |
| --- | --- | --- |
| **Lineare Algebra** | Vektoren, Matrizen, Matrix-Vektor-Produkt`\index{Matrix-Vektor-Produkt}`{=latex}, Eigenwerte | ab [Kapitel 2](#kap-fundament), zentral in [Kapitel 11](#kap-qp-nlp), [Kapitel 18](#kap-finanzdaten), [Kapitel 19](#kap-markowitz) |
-| **Analysis** | Ableitung, Gradient`\index{Gradient}`{=latex}, notwendige Bedingung erster Ordnung | [Kapitel 11](#kap-qp-nlp) |
+| **Analysis** | Ableitung, [Gradient](#gloss:gradient){.glossar-link}`\index{Gradient}`{=latex}, notwendige Bedingung erster Ordnung | [Kapitel 11](#kap-qp-nlp) |
| **Statistik** | Erwartungswert, Varianz, Standardabweichung, Quantil | [Kapitel 12](#kap-unsicherheit), 11–14 |
| **Python** | Listen, Dictionaries, Schleifen, Funktionen, Klassen, NumPy-Arrays | durchgängig |
@@ -105,7 +105,7 @@ pip install -r Operations_Research_mit_Python_Version_04_Programme/requirements.
Prüfen Sie mit diesem Skript, ob alles bereitsteht, **bevor** Sie mit [Kapitel 1](#kap-einfuehrung) beginnen. Es meldet für jedes Paket Version und Status und löst ein Mini-Optimierungsproblem.
-Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: `highspy` und `cvxpy` werden in der Paketübersicht nur auf Anwesenheit geprüft, und `ortools` lädt seine native Bibliothek zuerst. Der Grund: `ortools` und `highspy` bringen jeweils eine eigene HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und `cvxpy` importiert ein installiertes `highspy` bei der Solver-Erkennung selbst mit. Lädt deren HiGHS-Kopie zuerst, bricht der Test beim CP-SAT`\index{CP-SAT}`{=latex}-Funktionstest mit einem kryptischen `ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY`\index{CVXPY}`{=latex} verzichtet lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und Abhilfen: [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze) und [Anhang C](#anhang-fehlerdiagnose).
+Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: `highspy` und `cvxpy` werden in der Paketübersicht nur auf Anwesenheit geprüft, und `ortools` lädt seine native Bibliothek zuerst. Der Grund: `ortools` und `highspy` bringen jeweils eine eigene HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und `cvxpy` importiert ein installiertes `highspy` bei der [Solver](#gloss:solver){.glossar-link}-Erkennung selbst mit. Lädt deren HiGHS-Kopie zuerst, bricht der Test beim [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}-Funktionstest mit einem kryptischen `ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY`\index{CVXPY}`{=latex} verzichtet lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und Abhilfen: [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze) und [Anhang C](#anhang-fehlerdiagnose).
```python
#!/usr/bin/env python3
@@ -294,20 +294,20 @@ Suchen Sie sich einen aus. Sie können auch alle vier überspringen; nötig sind
* **Was Planen mit Durchschnitten kostet, wird hier beziffert, nicht behauptet.** Im Kraftwerkseinsatz aus [Kapitel 17](#kap-supplychain) kostet ein Plan, der mit dem Wind-**Erwartungswert** rechnet, in der Wirklichkeit **149 % mehr** als einer, der 40 Szenarien berücksichtigt — 874 870 € statt 351 356 € — und führt in **28 von 40 Szenarien** zum Lastabwurf statt in keinem.
* **„Wie sicher soll es sein?“ wird eine Preisfrage.** [Abschnitt 12.7](#sec:unsicherheit-chance-constraints) rechnet aus, was ein Prozentpunkt Versorgungssicherheit kostet: rund 56 000 € auf dem Weg zu 80 %, rund 254 000 € zwischen 95 und 99 %. Das **4,5-fache**. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.
-* **Für die Sitzung gibt es einen Bericht, keine Tabelle.** [Abschnitt 22.4](#sec:praxisfallen-attribution) beziffert je Nebenbedingung, was sie den Plan kostet, und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — *„warum ausgerechnet ich?“* — liefert [Kapitel 22](#kap-praxisfallen) den Constraint-Trace`\index{Constraint-Trace}`{=latex}, der die einzelne Zuweisung begründet.
+* **Für die Sitzung gibt es einen Bericht, keine Tabelle.** [Abschnitt 22.4](#sec:praxisfallen-attribution) beziffert je Nebenbedingung, was sie den Plan kostet, und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — *„warum ausgerechnet ich?“* — liefert [Kapitel 22](#kap-praxisfallen) den [Constraint-Trace](#gloss:constraint-trace){.glossar-link}`\index{Constraint-Trace}`{=latex}, der die einzelne Zuweisung begründet.
**Ihr Weg.** Zuerst Pfad E (2–4 Stunden): [Abschnitt 1.5](#sec:einfuehrung-historischer-kontext-und-evolution) und [Anhang B](#anhang-modellierungsmuster). Wenn Sie danach wissen wollen, was in Ihrem Betrieb geht, Pfad B und Projekt **P1** oder **P2**.
**Woran es scheitert.** Nicht am Modell, sondern an der Einführung — das ist der meistgenannte Befund in [Kapitel 22](#kap-praxisfallen). Und an einer Verwechslung, die dieses Buch Ihnen nicht abnehmen kann: Ein Bericht erklärt das **Modell**, nicht die **Wirklichkeit**. Ist ein Deckungsbeitrag falsch geschätzt, ist er überzeugend **und** falsch. Fragen Sie deshalb jedes Mal, worauf eine Zahl beruht.
-> **Eine Erwartung, die dieses Buch enttäuscht:** Es nennt keine ROI-Kennzahl und keine Amortisationsdauer. Beide hängen an Ihrem Betrieb, und jede Zahl hier wäre erfunden. Was es stattdessen liefert, ist das Werkzeug, Ihre eigene auszurechnen: den **EVPI** in [Kapitel 12](#kap-unsicherheit) (was eine perfekte Prognose überhaupt wert wäre — eine Obergrenze für jedes Prognoseprojekt) und die Kostenzurechnung aus [Abschnitt 22.4](#sec:praxisfallen-attribution).
+> **Eine Erwartung, die dieses Buch enttäuscht:** Es nennt keine ROI-Kennzahl und keine Amortisationsdauer. Beide hängen an Ihrem Betrieb, und jede Zahl hier wäre erfunden. Was es stattdessen liefert, ist das Werkzeug, Ihre eigene auszurechnen: den **[EVPI](#gloss:evpi-expected-value-of-perfect-information){.glossar-link}** in [Kapitel 12](#kap-unsicherheit) (was eine perfekte Prognose überhaupt wert wäre — eine Obergrenze für jedes Prognoseprojekt) und die Kostenzurechnung aus [Abschnitt 22.4](#sec:praxisfallen-attribution).
### Für Ingenieure und Produktionsplaner — die Physik des Systems {-}
> *„Der Engpass wandert. Kaum ist eine Maschine entlastet, steht die nächste — und niemand kann mir vorher sagen, welche.“*
-* **Der Engpass lässt sich benennen und bepreisen.** [Abschnitt 22.4](#sec:praxisfallen-attribution) rechnet für jede Kapazität aus, was die nächste Stunde wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis`\index{Schattenpreis}`{=latex} der Lackiererei exakt bis **+15 Stunden**; eine ganze Sonderschicht von 30 Stunden bringt deshalb 60 € statt der rechnerischen 120 €. **Die zweite Hälfte wäre bezahlt und wirkungslos.**
-* **Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.** [Kapitel 7](#kap-cpsat) modelliert Maschinenbelegung mit Intervallvariablen, [Kapitel 9](#kap-metaheuristiken) zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten Rüstzeit`\index{Rüstzeit}`{=latex}, CP-SAT allein 2 628, Simulated Annealing`\index{Simulated Annealing}`{=latex} 2 343, Annealing plus LNS **2 289** — und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
+* **Der Engpass lässt sich benennen und bepreisen.** [Abschnitt 22.4](#sec:praxisfallen-attribution) rechnet für jede Kapazität aus, was die nächste Stunde wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} der Lackiererei exakt bis **+15 Stunden**; eine ganze Sonderschicht von 30 Stunden bringt deshalb 60 € statt der rechnerischen 120 €. **Die zweite Hälfte wäre bezahlt und wirkungslos.**
+* **Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.** [Kapitel 7](#kap-cpsat) modelliert Maschinenbelegung mit Intervallvariablen, [Kapitel 9](#kap-metaheuristiken) zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex}, [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} allein 2 628, [Simulated Annealing](#gloss:simulated-annealing){.glossar-link}`\index{Simulated Annealing}`{=latex} 2 343, Annealing plus [LNS](#gloss:large-neighborhood-search-lns){.glossar-link} **2 289** — und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
* **Wenn der Solver zu lange braucht, gibt es Zahlen statt Ratschläge.** [Abschnitt 7.7](#sec:cpsat-parallele-suche) misst, was mehrere Suchstränge bringen (hier Faktor 12,3) — und ab wo mehr Arbeiter wieder schaden.
**Ihr Weg.** Pfad B bis [Kapitel 7](#kap-cpsat), dann Projekt **P5** (Produktionsplanung mit Rüstkosten). Wenn Ihre echten Instanzen den Solver stehen lassen: [Kapitel 9](#kap-metaheuristiken) und Projekt **P9**.
@@ -318,19 +318,19 @@ Suchen Sie sich einen aus. Sie können auch alle vier überspringen; nötig sind
> *„Die Optimierung liefert mir Gewichte, die kein Mensch halten würde: 60 % in einem Titel, null in fünf anderen. Und im nächsten Quartal sieht sie völlig anders aus.“*
-* **Der Grund dafür steht in [Kapitel 18](#kap-finanzdaten), nicht in [Kapitel 19](#kap-markowitz).** Eine aus kurzen Zeitreihen geschätzte Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} ist schlecht konditioniert; der Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel; ihre Wirkung wird an **Eigenwertspektrum und Konditionszahl**`\index{Konditionszahl}`{=latex} gemessen, nicht behauptet.
-* **Der VaR verschweigt genau das, worauf es ankommt.** In der Stichprobe aus [Kapitel 20](#kap-cvar) sagt der VaR: „an 95 % der Tage höchstens **1,86 %** Verlust“. Der CVaR sagt: „und wenn doch, dann im Mittel **2,99 %**“. Der schlechteste Tag der Stichprobe liegt bei **−23,0 %** — am VaR ändert dieser eine Tag **nichts**, am CVaR sehr wohl. Deshalb ist der CVaR die Größe, die man optimiert.
-* **Transaktionskosten`\index{Transaktionskosten}`{=latex} und Rebalancing sind Teil des Modells, nicht ein Abzug danach.** [Kapitel 20](#kap-cvar) nimmt sie in die Zielfunktion`\index{Zielfunktion}`{=latex}, [Kapitel 21](#kap-handelsmaschine) setzt den Backtest daneben — samt der Fallen, die einen guten Backtest wertlos machen.
+* **Der Grund dafür steht in [Kapitel 18](#kap-finanzdaten), nicht in [Kapitel 19](#kap-markowitz).** Eine aus kurzen Zeitreihen geschätzte [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} ist schlecht konditioniert; der Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel; ihre Wirkung wird an **Eigenwertspektrum und [Konditionszahl](#gloss:konditionszahl){.glossar-link}**`\index{Konditionszahl}`{=latex} gemessen, nicht behauptet.
+* **Der [VaR](#gloss:value-at-risk-var){.glossar-link} verschweigt genau das, worauf es ankommt.** In der Stichprobe aus [Kapitel 20](#kap-cvar) sagt der VaR: „an 95 % der Tage höchstens **1,86 %** Verlust“. Der [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} sagt: „und wenn doch, dann im Mittel **2,99 %**“. Der schlechteste Tag der Stichprobe liegt bei **−23,0 %** — am VaR ändert dieser eine Tag **nichts**, am CVaR sehr wohl. Deshalb ist der CVaR die Größe, die man optimiert.
+* **[Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} und [Rebalancing](#gloss:rebalancing){.glossar-link} sind Teil des Modells, nicht ein Abzug danach.** [Kapitel 20](#kap-cvar) nimmt sie in die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}, [Kapitel 21](#kap-handelsmaschine) setzt den [Backtest](#gloss:backtest){.glossar-link} daneben — samt der Fallen, die einen guten Backtest wertlos machen.
**Ihr Weg.** Pfad D, und zwar **mit** [Kapitel 18](#kap-finanzdaten). Danach Projekt **P6** (Portfolio-Rebalancer) oder **P7** (Risikoreport mit CVaR und Stresstests).
-**Woran es scheitert.** An der Reihenfolge. Wer bei Markowitz einsteigt und die Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler`\index{Schätzfehler}`{=latex} maximiert — und merkt es erst im Echtgeldbetrieb.
+**Woran es scheitert.** An der Reihenfolge. Wer bei Markowitz einsteigt und die Datenkapitel überspringt, bekommt ein Modell, das seine eigenen [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} maximiert — und merkt es erst im Echtgeldbetrieb.
### Für Studierende und Data Scientists — die Methodik {-}
> *„Ich kann ein Modell trainieren und den Fehler senken. Aber wie wird aus einer Prognose eine Entscheidung — und woher weiß ich, dass sie gut ist?“*
-* **Die unbequemste Erkenntnis zuerst.** [Kapitel 15](#kap-prognose) zeigt an einem gerechneten Fall, dass das Modell mit dem **schlechteren** MSE die **günstigere** Entscheidung trifft. Prognosegüte und Entscheidungsqualität sind zwei verschiedene Größen; wer die eine optimiert, bekommt die andere nicht geschenkt.
+* **Die unbequemste Erkenntnis zuerst.** [Kapitel 15](#kap-prognose) zeigt an einem gerechneten Fall, dass das Modell mit dem **schlechteren** [MSE](#gloss:mse){.glossar-link} die **günstigere** Entscheidung trifft. Prognosegüte und Entscheidungsqualität sind zwei verschiedene Größen; wer die eine optimiert, bekommt die andere nicht geschenkt.
* **Dieselbe Mathematik, zwei Welten.** [Kapitel 16](#kap-bruecke) lässt **denselben Code** einmal über eine Werkstatt und einmal über ein Depot laufen — und benennt die drei Stellen, an denen die Analogie endet. Struktur zu erkennen ist die eigentliche Fähigkeit, nicht Bibliothekskenntnis; zu wissen, wo eine Analogie aufhört, gehört dazu.
* **Es wird gerechnet, nicht nacherzählt.** Zu jedem Kapitel gehören Aufgaben mit vollständigen Lösungen, Handrechnungen und ein „Finde den Denkfehler“ — und jedes abgedruckte Ergebnis stammt aus einem echten Lauf des danebenstehenden Programms.
@@ -350,19 +350,19 @@ Sie müssen nicht alles lesen, um etwas Nützliches bauen zu können.
### Pfad B — Planung, Disposition, Personaleinsatz (ca. 25 Stunden) {-}
-[Kapitel 1](#kap-einfuehrung) (Bausteine) → [Kapitel 2](#kap-fundament) (bis [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)) → [Kapitel 4](#kap-modellierung) (vom Wunsch zum Modell) → [Kapitel 5](#kap-lp) (LP, Schattenpreise) → [Kapitel 6](#kap-milp) (Ja/Nein-Entscheidungen) → [Kapitel 7](#kap-cpsat) (CP-SAT, Scheduling) → Projekt **P1** (Vertretungsplaner) oder **P2** (Schichtplanung). Das ist der kürzeste Weg zu einem einsetzbaren Dienstplan-Optimierer.
+[Kapitel 1](#kap-einfuehrung) (Bausteine) → [Kapitel 2](#kap-fundament) (bis [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)) → [Kapitel 4](#kap-modellierung) (vom Wunsch zum Modell) → [Kapitel 5](#kap-lp) ([LP](#gloss:lineare-programmierung-lp){.glossar-link}, Schattenpreise) → [Kapitel 6](#kap-milp) (Ja/Nein-Entscheidungen) → [Kapitel 7](#kap-cpsat) (CP-SAT, Scheduling) → Projekt **P1** (Vertretungsplaner) oder **P2** (Schichtplanung). Das ist der kürzeste Weg zu einem einsetzbaren Dienstplan-Optimierer.
*Wenn der Solver bei Ihrer echten Instanz stehen bleibt:* [Kapitel 9](#kap-metaheuristiken) und Projekt **P9**.
### Pfad C — Logistik und Tourenplanung (ca. 25 Stunden) {-}
-[Kapitel 1](#kap-einfuehrung) → [Kapitel 2](#kap-fundament) (bis [Abschnitt 2.4](#sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder)) → [Kapitel 5](#kap-lp) → [Kapitel 6](#kap-milp) → [Kapitel 8](#kap-graphen) (Graphen, VRP) → Projekt **P3** (Liefertouren) oder **P4** (Lagernetzwerk).
+[Kapitel 1](#kap-einfuehrung) → [Kapitel 2](#kap-fundament) (bis [Abschnitt 2.4](#sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder)) → [Kapitel 5](#kap-lp) → [Kapitel 6](#kap-milp) → [Kapitel 8](#kap-graphen) ([Graphen](#gloss:graphen){.glossar-link}, [VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link}) → Projekt **P3** (Liefertouren) oder **P4** (Lagernetzwerk).
*Wenn die Instanzen zu groß werden:* [Kapitel 9](#kap-metaheuristiken) (gute Lösung in fester Zeit) und [Kapitel 10](#kap-dekomposition) (das Modell umbauen statt die Lösung raten) — dazu Projekt **P9**, das beide Wege am selben Problem vergleicht.
### Pfad D — Quantitative Finance (ca. 30 Stunden) {-}
-[Kapitel 1](#kap-einfuehrung) → [Kapitel 2](#kap-fundament) → [Kapitel 11](#kap-qp-nlp) (QP, KKT) → [Kapitel 18](#kap-finanzdaten) (Daten, Shrinkage) → [Kapitel 19](#kap-markowitz) (Markowitz) → [Kapitel 20](#kap-cvar) (CVaR) → [Kapitel 21](#kap-handelsmaschine) (Backtest) → Projekt **P6** (Portfolio-Rebalancer) oder **P7** (Risikoreport). **Wichtig:** Überspringen Sie [Kapitel 18](#kap-finanzdaten) nicht. Wer direkt bei Markowitz einsteigt, optimiert Schätzrauschen und wundert sich über absurde Gewichte.
+[Kapitel 1](#kap-einfuehrung) → [Kapitel 2](#kap-fundament) → [Kapitel 11](#kap-qp-nlp) ([QP](#gloss:quadratische-programmierung-qp){.glossar-link}, [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}) → [Kapitel 18](#kap-finanzdaten) (Daten, Shrinkage) → [Kapitel 19](#kap-markowitz) (Markowitz) → [Kapitel 20](#kap-cvar) (CVaR) → [Kapitel 21](#kap-handelsmaschine) (Backtest) → Projekt **P6** (Portfolio-Rebalancer) oder **P7** (Risikoreport). **Wichtig:** Überspringen Sie [Kapitel 18](#kap-finanzdaten) nicht. Wer direkt bei Markowitz einsteigt, optimiert Schätzrauschen und wundert sich über absurde Gewichte.
### Pfad E — Ich habe morgen ein konkretes Problem (2–4 Stunden) {-}
@@ -384,53 +384,53 @@ Alle Beispielprogramme dieses Buchs im Überblick, sortiert nach Kapitel — pra
| --- | --- | --- |
| `Installationstest.py` | Installationsprüfung | Vorspann |
| `Bot_Allokation.py` | Erstes CP-SAT-Modell | [Kapitel 1](#kap-einfuehrung) |
-| `Brute_Force_Vergleich.py` | Kombinatorische Explosion`\index{Kombinatorische Explosion}`{=latex} | [Kapitel 1](#kap-einfuehrung) |
+| `Brute_Force_Vergleich.py` | [Kombinatorische Explosion](#gloss:kombinatorische-explosion){.glossar-link}`\index{Kombinatorische Explosion}`{=latex} | [Kapitel 1](#kap-einfuehrung) |
| `Bausteine_Vorlage.py` | Vorlage für eigene Modelle | [Kapitel 1](#kap-einfuehrung) |
| `Excel_Bruecke.py` | Excel-Mappe lesen, lösen, zurückschreiben | [Kapitel 1](#kap-einfuehrung) |
| `Matrixform.py` | Matrixform`\index{Matrixform}`{=latex} und Zulässigkeit | [Kapitel 2](#kap-fundament) |
| `Konvexitaet_Demo.py` | Sehnen-Test, lokale Optima | [Kapitel 2](#kap-fundament) |
-| `Visualisierung_Loesungsraum.py` | Polyeder`\index{Polyeder}`{=latex} mit Ecken | [Kapitel 2](#kap-fundament) |
+| `Visualisierung_Loesungsraum.py` | [Polyeder](#gloss:polyeder){.glossar-link}`\index{Polyeder}`{=latex} mit Ecken | [Kapitel 2](#kap-fundament) |
| `Skalierung_Kondition.py` | Konditionszahl, Ruiz, Toleranzen | [Kapitel 2](#kap-fundament) |
| `Solver_Wahl.py` | Entscheidungshilfe | [Kapitel 3](#kap-oekosystem) |
| `Ein_System_Vier_Ansaetze.py` | Vier Bibliotheken | [Kapitel 3](#kap-oekosystem) |
-| `Modellierungsschichten.py` | Pyomo`\index{Pyomo}`{=latex} und Linopy`\index{Linopy}`{=latex} | [Kapitel 3](#kap-oekosystem) |
+| `Modellierungsschichten.py` | [Pyomo](#gloss:pyomo){.glossar-link}`\index{Pyomo}`{=latex} und [Linopy](#gloss:linopy){.glossar-link}`\index{Linopy}`{=latex} | [Kapitel 3](#kap-oekosystem) |
| `Vektorisierte_Modellgenerierung.py` | Aufbauzeit vs. Lösezeit | [Kapitel 3](#kap-oekosystem) |
| `Vom_Wunsch_zum_Modell.py` | Fünf Modelle auf denselben Daten | [Kapitel 4](#kap-modellierung) |
| `Simplex_Tableau_LP.py` | Simplex von Grund auf | [Kapitel 5](#kap-lp) |
| `Sensitivitaetsanalyse.py` | Schattenpreise | [Kapitel 5](#kap-lp) |
| `Dualitaet_Nachweis.py` | Primal-dual, starke Dualität | [Kapitel 5](#kap-lp) |
-| `Toleranzen_und_Entartung.py` | Entartung`\index{Entartung}`{=latex}, Toleranzen, Dualspannen | [Kapitel 5](#kap-lp) |
+| `Toleranzen_und_Entartung.py` | [Entartung](#gloss:entartung){.glossar-link}`\index{Entartung}`{=latex}, Toleranzen, Dualspannen | [Kapitel 5](#kap-lp) |
| `Runden_Gegenbeispiel.py` | Warum Runden scheitert | [Kapitel 6](#kap-milp) |
| `Rucksack.py` | Knapsack, LP-Schranke | [Kapitel 6](#kap-milp) |
| `MILP_Portfolio_Fixgebuehren.py` | Fixkosten, Kardinalität | [Kapitel 6](#kap-milp) |
-| `Solverstatus_und_Gap.py` | MIP-Gap`\index{MIP-Gap}`{=latex}, Zeitlimit, Statusfälle | [Kapitel 6](#kap-milp) |
+| `Solverstatus_und_Gap.py` | [MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex}, Zeitlimit, Statusfälle | [Kapitel 6](#kap-milp) |
| `Warmstart_Effekt.py` | LPT-Hinweis für CP-SAT | [Kapitel 6](#kap-milp) |
-| `Big_M_Falle.py` | Trickle Flow`\index{Trickle Flow}`{=latex} bei zu großem M | [Kapitel 6](#kap-milp) |
+| `Big_M_Falle.py` | [Trickle Flow](#gloss:trickle-flow){.glossar-link}`\index{Trickle Flow}`{=latex} bei zu großem M | [Kapitel 6](#kap-milp) |
| `Propagation_Demo.py` | Propagation messbar | [Kapitel 7](#kap-cpsat) |
| `CP_SAT_Vertretungssystem.py` | Vertretungsplan | [Kapitel 7](#kap-cpsat) |
-| `JobShop_Intervalle.py` | Job-Shop-Scheduling`\index{Job-Shop-Scheduling}`{=latex} | [Kapitel 7](#kap-cpsat) |
+| `JobShop_Intervalle.py` | [Job-Shop-Scheduling](#gloss:job-shop-scheduling){.glossar-link}`\index{Job-Shop-Scheduling}`{=latex} | [Kapitel 7](#kap-cpsat) |
| `Parallele_Suche.py` | num_workers: Tempo gegen Reproduzierbarkeit | [Kapitel 7](#kap-cpsat) |
| `CP_SAT_Statusfaelle.py` | Die fünf Solver-Antworten | [Kapitel 7](#kap-cpsat) |
| `Strafgewichte.py` | Gewichte als Wechselkurse | [Kapitel 7](#kap-cpsat) |
| `Min_Cost_Flow.py` | Netzwerkfluss | [Kapitel 8](#kap-graphen) |
| `Zuordnung_Ungarisch.py` | Zuordnung, Unimodularität | [Kapitel 8](#kap-graphen) |
-| `VRP_Flotten_Routing.py` | CVRPTW`\index{CVRPTW}`{=latex} | [Kapitel 8](#kap-graphen) |
+| `VRP_Flotten_Routing.py` | [CVRPTW](#gloss:cvrptw){.glossar-link}`\index{CVRPTW}`{=latex} | [Kapitel 8](#kap-graphen) |
| `VRP_Kapazitaetsfalle.py` | Vergessene Dimension im Routing | [Kapitel 8](#kap-graphen) |
| `Simulated_Annealing.py` | Lokale Suche, Temperatur kalibrieren | [Kapitel 9](#kap-metaheuristiken) |
| `Metaheuristik_vs_Exakt.py` | Der Umschlagpunkt, und die Schranke | [Kapitel 9](#kap-metaheuristiken) |
| `Large_Neighborhood_Search.py` | Zerstören und exakt reparieren | [Kapitel 9](#kap-metaheuristiken) |
| `Spaltengenerierung.py` | Muster statt Stücke, Master und Pricing | [Kapitel 10](#kap-dekomposition) |
-| `QP_Grundlagen.py` | Konvexität`\index{Konvexität}`{=latex}, DCP-Check | [Kapitel 11](#kap-qp-nlp) |
+| `QP_Grundlagen.py` | [Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex}, [DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link}-Check | [Kapitel 11](#kap-qp-nlp) |
| `KKT_Nachweis.py` | KKT numerisch prüfen | [Kapitel 11](#kap-qp-nlp) |
-| `Entropie_Maximierte_Allokation.py` | NLP mit Entropie | [Kapitel 11](#kap-qp-nlp) |
+| `Entropie_Maximierte_Allokation.py` | [NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link} mit Entropie | [Kapitel 11](#kap-qp-nlp) |
| `Lokale_Optima_Multistart.py` | Lokale Optima, Multistart | [Kapitel 11](#kap-qp-nlp) |
| `Fluch_des_Durchschnitts.py` | Optimum ≠ Mittelwert | [Kapitel 12](#kap-unsicherheit) |
| `Monte_Carlo.py` | Monte-Carlo-Bewertung | [Kapitel 12](#kap-unsicherheit) |
| `Stochastische_Optimierung.py` | Two-Stage mit Recourse | [Kapitel 12](#kap-unsicherheit) |
| `Robuste_Optimierung.py` | Worst-Case-Absicherung | [Kapitel 12](#kap-unsicherheit) |
| `Chance_Constraints.py` | Zusage „mit 95 % Sicherheit“, SOC und Big-M | [Kapitel 12](#kap-unsicherheit) |
-| `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion`\index{Rückwärtsinduktion}`{=latex} | [Kapitel 13](#kap-dynamische-programmierung) |
-| `Mehrziel_Pareto.py` | Pareto-Front`\index{Pareto-Front}`{=latex}, ε-Constraint, Gewichtslücke | [Kapitel 14](#kap-mehrziel) |
+| `Bellman_Minimalbeispiel.py` | [Rückwärtsinduktion](#gloss:rueckwaertsinduktion){.glossar-link}`\index{Rückwärtsinduktion}`{=latex} | [Kapitel 13](#kap-dynamische-programmierung) |
+| `Mehrziel_Pareto.py` | [Pareto-Front](#gloss:pareto-front){.glossar-link}`\index{Pareto-Front}`{=latex}, ε-Constraint, Gewichtslücke | [Kapitel 14](#kap-mehrziel) |
| `Predict_then_Optimize.py` | MSE gegen Entscheidungskosten | [Kapitel 15](#kap-prognose) |
| `Strukturbruecke.py` | Derselbe Code über Werkstatt und Depot | [Kapitel 16](#kap-bruecke) |
| `Kraftwerkseinsatz.py` | Unit Commitment unter Windunsicherheit | [Kapitel 17](#kap-supplychain) |
@@ -439,12 +439,12 @@ Alle Beispielprogramme dieses Buchs im Überblick, sortiert nach Kapitel — pra
| `Schaetzrauschen_Demo.py` | Error-Maximizer messen | [Kapitel 18](#kap-finanzdaten) |
| `Finanzdaten_Ledoit_Wolf.py` | Datenpipeline, Shrinkage | [Kapitel 18](#kap-finanzdaten) |
| `Kovarianz_Falle.py` | Singuläre Kovarianz, Error-Maximizer | [Kapitel 18](#kap-finanzdaten) |
-| `Markowitz_CVXPY.py` | GMV, Max Sharpe, Frontier | [Kapitel 19](#kap-markowitz) |
+| `Markowitz_CVXPY.py` | [GMV](#gloss:global-minimum-variance-portfolio-gmv){.glossar-link}, Max Sharpe, Frontier | [Kapitel 19](#kap-markowitz) |
| `Diversifikation_Demo.py` | Korrelation und Portfoliorisiko | [Kapitel 19](#kap-markowitz) |
| `Renditeschaetzung_Falle.py` | Schätzfehler in erwarteten Renditen | [Kapitel 19](#kap-markowitz) |
-| `VaR_CVaR_Demo.py` | Fat Tails`\index{Fat Tails}`{=latex}, Subadditivität`\index{Subadditivität}`{=latex} | [Kapitel 20](#kap-cvar) |
+| `VaR_CVaR_Demo.py` | [Fat Tails](#gloss:fat-tails){.glossar-link}`\index{Fat Tails}`{=latex}, [Subadditivität](#gloss:subadditivitaet){.glossar-link}`\index{Subadditivität}`{=latex} | [Kapitel 20](#kap-cvar) |
| `CVaR_Portfolio.py` | CVaR mit Reibung | [Kapitel 20](#kap-cvar) |
-| `QuantitativeTradingEngine.py` | Walk-Forward-Backtest`\index{Walk-Forward-Backtest}`{=latex} | [Kapitel 21](#kap-handelsmaschine) |
+| `QuantitativeTradingEngine.py` | [Walk-Forward-Backtest](#gloss:walk-forward-backtest){.glossar-link}`\index{Walk-Forward-Backtest}`{=latex} | [Kapitel 21](#kap-handelsmaschine) |
| `Backtest_Fallen.py` | Fünf Selbsttäuschungen | [Kapitel 21](#kap-handelsmaschine) |
| `Data_Snooping.py` | Bestes aus N Versuchen auf Rauschen | [Kapitel 21](#kap-handelsmaschine) |
| `Infeasibility_Diagnose.py` | Notfallplan statt Fehler | [Kapitel 22](#kap-praxisfallen) |
@@ -457,13 +457,13 @@ Alle Beispielprogramme dieses Buchs im Überblick, sortiert nach Kapitel — pra
| `Mutationstest.py` | Findet die Lücken der eigenen Tests | [Kapitel 23](#kap-testing) |
| `Benchmark_Skalierung.py` | Aufbau gegen Lösen, vier Bibliotheken | [Kapitel 23](#kap-testing) |
| `Optimierungsdienst.py` | Das Modell als HTTP-Dienst | [Kapitel 23](#kap-testing) |
-| `Konfliktsuche.py` | Deletion Filter`\index{Deletion Filter}`{=latex}: den Widerspruch einkreisen | [Anhang C](#anhang-fehlerdiagnose) |
+| `Konfliktsuche.py` | [Deletion Filter](#gloss:deletion-filter){.glossar-link}`\index{Deletion Filter}`{=latex}: den Widerspruch einkreisen | [Anhang C](#anhang-fehlerdiagnose) |
---
## Was dieses Buch nicht ist {-}
-Es ist **keine Anleitung zum Bau eigener Solver** — wir nutzen ausgereifte Implementierungen und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der Simplex-Algorithmus`\index{Simplex-Algorithmus}`{=latex} in [Kapitel 5](#kap-lp), den wir von Grund auf programmieren, weil man ihn verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
+Es ist **keine Anleitung zum Bau eigener Solver** — wir nutzen ausgereifte Implementierungen und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der [Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}`\index{Simplex-Algorithmus}`{=latex} in [Kapitel 5](#kap-lp), den wir von Grund auf programmieren, weil man ihn verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
Es ist **kein Buch über maschinelles Lernen**. Prognosen kommen nur so weit vor, wie sie als Eingangsdaten einer Optimierung nötig sind.
@@ -506,21 +506,21 @@ Dieses Kapitel ist zum Nachschlagen gedacht, nicht zum Durchlesen. Schlagen Sie
| Symbol | Sprechweise | Bedeutung | Beispiel aus dem Buch |
| --- | --- | --- | --- |
-| $x$ | „iks“ | **Entscheidungsvariable`\index{Entscheidungsvariable}`{=latex}(n)** — das, was der Solver festlegen darf | Anzahl gestarteter Trading-Bots ([Kapitel 1](#kap-einfuehrung)) |
+| $x$ | „iks“ | **Entscheidungsvariable`\index{Entscheidungsvariable}`{=latex}(n)** — das, was der [Solver](#gloss:solver){.glossar-link} festlegen darf | Anzahl gestarteter Trading-Bots ([Kapitel 1](#kap-einfuehrung)) |
| $\mathbf{x}$ | „Vektor iks“ | Alle Entscheidungsvariablen als Spaltenvektor | $\mathbf{x} = (x_1, \dots, x_n)^\top$ |
| $n$ | „en“ | Anzahl der Entscheidungsvariablen | 10 Aktien $\Rightarrow n = 10$ |
| $m$ | „em“ | Anzahl der Nebenbedingungen | 3 Ressourcen $\Rightarrow m = 3$ |
-| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der Zielfunktion`\index{Zielfunktion}`{=latex} | Gewinn je Bot-Typ: $(150, 250)^\top$ |
-| $\mathbf{A}$ | „Matrix a“ | **Technologiematrix**`\index{Technologiematrix}`{=latex} — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
-| $\mathbf{b}$ | „Vektor be“ | **Kapazitätsvektor**`\index{Kapazitätsvektor}`{=latex}, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
+| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} | Gewinn je Bot-Typ: $(150, 250)^\top$ |
+| $\mathbf{A}$ | „Matrix a“ | **[Technologiematrix](#gloss:technologiematrix){.glossar-link}**`\index{Technologiematrix}`{=latex} — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
+| $\mathbf{b}$ | „Vektor be“ | **[Kapazitätsvektor](#gloss:kapazitaetsvektor){.glossar-link}**`\index{Kapazitätsvektor}`{=latex}, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
| $f(\mathbf{x})$ | „ef von iks“ | **Zielfunktion** — die eine Zahl, die bewertet wird | Tagesgewinn in Euro |
| $g_i(\mathbf{x})$ | „ge i von iks“ | $i$-te **Ungleichungs-Nebenbedingung**, Form $g_i(\mathbf{x}) \le 0$ | „RAM-Verbrauch minus 60 GB“ |
| $h_j(\mathbf{x})$ | „ha j von iks“ | $j$-te **Gleichungs-Nebenbedingung**, Form $h_j(\mathbf{x}) = 0$ | „Summe der Gewichte minus 1“ |
| $s_i$ | „es i“ | **Schlupfvariable**`\index{Schlupfvariable}`{=latex} — ungenutzte Reserve der Ressource $i$ | 1 GB RAM übrig $\Rightarrow s_2 = 1$ |
-| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / Schattenpreis**`\index{Schattenpreis}`{=latex} der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
+| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / [Schattenpreis](#gloss:schattenpreis){.glossar-link}**`\index{Schattenpreis}`{=latex} der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
| $M$ | „groß em“ | **Big-M** — hinreichend große Konstante zum Ein-/Ausschalten von Bedingungen | $x_j \le M \cdot y_j$ |
| $z_i,\ y_i$ | | **Binärvariable**`\index{Binärvariable}`{=latex} $\in \{0,1\}$ — Ja/Nein-Schalter | „Position $i$ wird eröffnet“ |
-| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge`\index{Konvexität!Menge}`{=latex} aller erlaubten Lösungen | das Polyeder`\index{Polyeder}`{=latex} aus [Kapitel 2](#kap-fundament) |
+| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge`\index{Konvexität!Menge}`{=latex} aller erlaubten Lösungen | das [Polyeder](#gloss:polyeder){.glossar-link}`\index{Polyeder}`{=latex} aus [Kapitel 2](#kap-fundament) |
| $Z,\ Z^*$ | „zet“, „zet Stern“ | Zielfunktionswert bzw. **optimaler** Zielfunktionswert | $Z^* = 2300$ € |
| $\mathbf{x}^*$ | „iks Stern“ | Die **optimale Lösung**. Der Stern markiert immer „im Optimum“ | $\mathbf{x}^* = (7, 5)^\top$ |
@@ -528,9 +528,9 @@ Dieses Kapitel ist zum Nachschlagen gedacht, nicht zum Durchlesen. Schlagen Sie
| Symbol | Bedeutung | Praktische Folge |
| --- | --- | --- |
-| $\mathbb{R}$ | Reelle Zahlen (beliebig teilbar) | Variable darf 3,7 sein — **LP**, schnell lösbar |
+| $\mathbb{R}$ | Reelle Zahlen (beliebig teilbar) | Variable darf 3,7 sein — **[LP](#gloss:lineare-programmierung-lp){.glossar-link}**, schnell lösbar |
| $\mathbb{R}^n$ | $n$-Tupel reeller Zahlen | Der Raum, in dem $\mathbf{x}$ lebt |
-| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **MILP**, NP-schwer`\index{NP-schwer}`{=latex} |
+| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **[MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}**, [NP-schwer](#gloss:np-schwer){.glossar-link}`\index{NP-schwer}`{=latex} |
| $\mathbb{N}_0$ | Natürliche Zahlen einschließlich 0 | Stückzahlen, nie negativ |
| $\{0, 1\}$ | Nur zwei Werte | Ja/Nein-Entscheidung |
| $\in$ | „ist Element von“ | $x \in \mathbb{Z}$: „$x$ ist ganzzahlig“ |
@@ -544,12 +544,12 @@ Dieses Kapitel ist zum Nachschlagen gedacht, nicht zum Durchlesen. Schlagen Sie
| Symbol | Bedeutung | Lesehilfe |
| --- | --- | --- |
| $\sum_{i=1}^n a_i$ | Summe | „addiere $a_1$ bis $a_n$“ |
-| $\mathbf{c}^\top \mathbf{x}$ | Skalarprodukt`\index{Skalarprodukt}`{=latex} | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
+| $\mathbf{c}^\top \mathbf{x}$ | [Skalarprodukt](#gloss:skalarprodukt){.glossar-link}`\index{Skalarprodukt}`{=latex} | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
| $^\top$ | Transponiert | dreht Zeilen und Spalten; macht aus Spalten- einen Zeilenvektor |
| $\mathbf{A}\mathbf{x}$ | Matrix-Vektor-Produkt`\index{Matrix-Vektor-Produkt}`{=latex} | „berechne alle Ressourcenverbräuche auf einmal“ |
| $\min_x,\ \max_x$ | Minimum/Maximum über $x$ | „wähle $x$ so, dass … kleinst-/größtmöglich wird“ |
| $\arg\min$ | Argument des Minimums | nicht der *Wert*, sondern die *Stelle* des Minimums |
-| $\nabla f$ | „Nabla ef“, Gradient`\index{Gradient}`{=latex} | Vektor aller partiellen Ableitungen — zeigt bergauf |
+| $\nabla f$ | „Nabla ef“, [Gradient](#gloss:gradient){.glossar-link}`\index{Gradient}`{=latex} | Vektor aller partiellen Ableitungen — zeigt bergauf |
| $\partial Z / \partial b_i$ | Partielle Ableitung | „wie ändert sich $Z$, wenn nur $b_i$ sich ändert?“ |
| $\Vert \mathbf{v} \Vert_1$ | $L_1$-Norm | Summe der Beträge: $\vert v_1\vert + \vert v_2\vert + \dots$ |
| $\Vert \mathbf{v} \Vert_2$ | $L_2$-Norm (euklidisch) | die gewöhnliche Länge eines Pfeils |
@@ -567,18 +567,18 @@ Dieses Kapitel ist zum Nachschlagen gedacht, nicht zum Durchlesen. Schlagen Sie
| $\mathbf{w}$ | **Portfoliogewichte** — Kapitalanteile je Titel | dimensionslos, Summe $= 1$ |
| $w_i$ | Anteil von Titel $i$ | $0{,}15 \equiv 15\,\%$ |
| $\boldsymbol{\mu}$ | „mü“ — Vektor der **erwarteten Renditen** | z. B. $0{,}11$ = 11 % p. a. |
-| $\boldsymbol{\Sigma}$ | „Sigma“ — **Kovarianzmatrix**`\index{Kovarianzmatrix}`{=latex} der Renditen | $N \times N$, Einheit Rendite² |
-| $\sigma$ | „sigma“ — **Volatilität**`\index{Volatilität}`{=latex} (Standardabweichung) | $0{,}20$ = 20 % p. a. |
+| $\boldsymbol{\Sigma}$ | „Sigma“ — **[Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}**`\index{Kovarianzmatrix}`{=latex} der Renditen | $N \times N$, Einheit Rendite² |
+| $\sigma$ | „sigma“ — **[Volatilität](#gloss:volatilitaet){.glossar-link}**`\index{Volatilität}`{=latex} (Standardabweichung) | $0{,}20$ = 20 % p. a. |
| $\sigma^2$ | **Varianz** | Quadrat der Volatilität |
| $\mathbf{S}$ | **Stichproben**-Kovarianzmatrix (aus Daten geschätzt) | verrauscht |
| $\mathbf{F}$ | **Shrinkage-Target** — strukturierte Vergleichsmatrix | verzerrt, aber stabil |
| $\delta$ | „delta“ — Shrinkage-Intensität | $\in [0,1]$, z. B. $0{,}18$ |
| $r_f$ | *risk-free rate*, **risikoloser Zins** | z. B. $0{,}03$ = 3 % p. a. |
-| $R_{i,t}$ | **Diskrete Rendite**`\index{Diskrete Rendite}`{=latex} von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
-| $r_{i,t}$ | **Logarithmische Rendite**`\index{Logarithmische Rendite}`{=latex} (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
+| $R_{i,t}$ | **[Diskrete Rendite](#gloss:diskrete-rendite){.glossar-link}**`\index{Diskrete Rendite}`{=latex} von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
+| $r_{i,t}$ | **[Logarithmische Rendite](#gloss:logarithmische-rendite){.glossar-link}**`\index{Logarithmische Rendite}`{=latex} (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
| $P_{i,t}$ | **Kurs** (bereinigter Schlusskurs) von Titel $i$ | in Währungseinheiten |
| $\lambda$ | Risikoaversion / Gewichtung des Risikoterms | Modellparameter, frei wählbar |
-| $\alpha$ | Konfidenzniveau bei VaR/CVaR | $0{,}95$ = „schlechteste 5 % der Tage“ |
+| $\alpha$ | Konfidenzniveau bei [VaR](#gloss:value-at-risk-var){.glossar-link}/[CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} | $0{,}95$ = „schlechteste 5 % der Tage“ |
| $\gamma$ | Hilfsvariable, schätzt den VaR ([Kapitel 20](#kap-cvar)); Diskontfaktor ([Kapitel 13](#kap-dynamische-programmierung)) | kontextabhängig |
| $\eta$ | „eta“ — Marktimpact-Koeffizient | [Kapitel 13](#kap-dynamische-programmierung), Almgren-Chriss |
| $X_t$ | Verbleibender Aktienbestand zum Zeitpunkt $t$ | [Kapitel 13](#kap-dynamische-programmierung) |
@@ -595,7 +595,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| --- | --- | --- | --- |
| $\pi$ | „pi“ | **Reihenfolge (Permutation)**: $\pi(k)$ ist der Auftrag, der als $k$-ter läuft | [Kapitel 9](#kap-metaheuristiken) |
| $\Delta$ | „delta“ | **Zugbewertung** — um wie viel ein Nachbarschaftszug die Lösung verschlechtert ($\Delta > 0$) oder verbessert ($\Delta \le 0$) | [Kapitel 9](#kap-metaheuristiken) |
-| $T$ | „te“ | **Temperatur** im Simulated Annealing`\index{Simulated Annealing}`{=latex} — steuert, wie oft eine Verschlechterung angenommen wird | [Kapitel 9](#kap-metaheuristiken) |
+| $T$ | „te“ | **Temperatur** im [Simulated Annealing](#gloss:simulated-annealing){.glossar-link}`\index{Simulated Annealing}`{=latex} — steuert, wie oft eine Verschlechterung angenommen wird | [Kapitel 9](#kap-metaheuristiken) |
| $e^{-\Delta/T}$ | | **Annahmewahrscheinlichkeit** (Metropolis-Kriterium): kleine Verschlechterungen fast immer, große fast nie | [Kapitel 9](#kap-metaheuristiken) |
| $\pi_i$ | „pi i“ | **Dualpreis** der Bedingung $i$ im Master-LP — der Preis, mit dem das Pricing rechnet | [Kapitel 10](#kap-dekomposition) |
| $a_i$ | „a i“ | Wie oft Stück $i$ in einem **Schnittmuster** vorkommt (eine Spalte des Masters) | [Kapitel 10](#kap-dekomposition) |
@@ -608,7 +608,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| $L_k$ | „el k“ | **Mindestlaufzeit** von Anlage $k$ in Stunden | [Kapitel 17](#kap-supplychain) |
| $\tau$ | „tau“ | Laufender **Zeitindex innerhalb eines Fensters** — dort, wo $t$ schon den Fensteranfang bezeichnet | [Kapitel 17](#kap-supplychain) |
-> **⚠️ Achtung, Doppelbelegungen.** In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: $\lambda$ ist in [Kapitel 5](#kap-lp) und [Kapitel 11](#kap-qp-nlp) ein **Lagrange-Multiplikator`\index{Lagrange-Multiplikator}`{=latex}/Schattenpreis**, in [Kapitel 19](#kap-markowitz) und [Kapitel 20](#kap-cvar) ein **frei gewählter Risikoaversionsparameter**. $\gamma$ ist in [Kapitel 13](#kap-dynamische-programmierung) der **Diskontfaktor**, in [Kapitel 20](#kap-cvar) die **VaR-Hilfsvariable**. $S$ bezeichnet als Matrix $\mathbf{S}$ die Stichprobenkovarianz, als Skalar $S$ die Szenarienzahl. $\pi$ ist in [Kapitel 9](#kap-metaheuristiken) eine **Reihenfolge**, in [Kapitel 10](#kap-dekomposition) ein **Dualpreis** — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
+> **⚠️ Achtung, Doppelbelegungen.** In der Literatur — und deshalb auch hier — tragen einige Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle: $\lambda$ ist in [Kapitel 5](#kap-lp) und [Kapitel 11](#kap-qp-nlp) ein **[Lagrange-Multiplikator](#gloss:lagrange-multiplikator){.glossar-link}`\index{Lagrange-Multiplikator}`{=latex}/Schattenpreis**, in [Kapitel 19](#kap-markowitz) und [Kapitel 20](#kap-cvar) ein **frei gewählter Risikoaversionsparameter**. $\gamma$ ist in [Kapitel 13](#kap-dynamische-programmierung) der **Diskontfaktor**, in [Kapitel 20](#kap-cvar) die **VaR-Hilfsvariable**. $S$ bezeichnet als Matrix $\mathbf{S}$ die Stichprobenkovarianz, als Skalar $S$ die Szenarienzahl. $\pi$ ist in [Kapitel 9](#kap-metaheuristiken) eine **Reihenfolge**, in [Kapitel 10](#kap-dekomposition) ein **Dualpreis** — beide Bedeutungen sind so verbreitet, dass ein Ausweichbuchstabe mehr verwirren als helfen würde. Der Kontext ist jeweils eindeutig, und die Kapitel weisen an der betreffenden Stelle darauf hin.
---
@@ -623,52 +623,52 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **MILP** | Mixed-Integer Linear Program(ming) | Gemischt-ganzzahlige lineare Optimierung | [Kapitel 6](#kap-milp) |
| **MIP** | Mixed-Integer Program(ming) | Oberbegriff, meist synonym zu MILP | [Kapitel 6](#kap-milp) |
| **IP** | Integer Program(ming) | Rein ganzzahlige Optimierung | [Kapitel 6](#kap-milp) |
-| **QP** | Quadratic Program(ming) | Quadratische Optimierung | [Kapitel 11](#kap-qp-nlp) |
-| **MIQP** | Mixed-Integer Quadratic Program(ming) | Ganzzahlig-quadratische Optimierung | [Kapitel 19](#kap-markowitz) |
-| **NLP** | Nonlinear Program(ming) | Nichtlineare Optimierung | [Kapitel 11](#kap-qp-nlp) |
-| **MINLP**`\index{MINLP}`{=latex} | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | [Kapitel 3](#kap-oekosystem) |
-| **CP** | Constraint Programming | Bedingungsprogrammierung | [Kapitel 7](#kap-cpsat) |
-| **SAT** | Boolean **Sat**isfiability Problem | Erfüllbarkeitsproblem der Aussagenlogik | [Kapitel 7](#kap-cpsat) |
-| **CP-SAT**`\index{CP-SAT}`{=latex} | Constraint Programming über SAT-Techniken | Solver-Name von Google OR-Tools`\index{OR-Tools}`{=latex} | [Kapitel 7](#kap-cpsat) |
-| **CDCL**`\index{CDCL}`{=latex} | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | [Kapitel 7](#kap-cpsat) |
-| **DP** | Dynamic Programming | Dynamische Programmierung | [Kapitel 13](#kap-dynamische-programmierung) |
+| **[QP](#gloss:quadratische-programmierung-qp){.glossar-link}** | Quadratic Program(ming) | Quadratische Optimierung | [Kapitel 11](#kap-qp-nlp) |
+| **[MIQP](#gloss:miqp){.glossar-link}** | Mixed-Integer Quadratic Program(ming) | Ganzzahlig-quadratische Optimierung | [Kapitel 19](#kap-markowitz) |
+| **[NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link}** | Nonlinear Program(ming) | Nichtlineare Optimierung | [Kapitel 11](#kap-qp-nlp) |
+| **[MINLP](#gloss:minlp){.glossar-link}**`\index{MINLP}`{=latex} | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | [Kapitel 3](#kap-oekosystem) |
+| **[CP](#gloss:constraint-programming-cp){.glossar-link}** | Constraint Programming | Bedingungsprogrammierung | [Kapitel 7](#kap-cpsat) |
+| **[SAT](#gloss:sat-boolean-satisfiability){.glossar-link}** | Boolean **Sat**isfiability Problem | Erfüllbarkeitsproblem der Aussagenlogik | [Kapitel 7](#kap-cpsat) |
+| **[CP-SAT](#gloss:cp-sat){.glossar-link}**`\index{CP-SAT}`{=latex} | Constraint Programming über SAT-Techniken | Solver-Name von Google [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} | [Kapitel 7](#kap-cpsat) |
+| **[CDCL](#gloss:cdcl){.glossar-link}**`\index{CDCL}`{=latex} | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | [Kapitel 7](#kap-cpsat) |
+| **[DP](#gloss:dynamische-programmierung-dp){.glossar-link}** | Dynamic Programming | Dynamische Programmierung | [Kapitel 13](#kap-dynamische-programmierung) |
| **B&B** | Branch and Bound | Verzweigen und Beschränken | [Kapitel 6](#kap-milp) |
| **B&C** | Branch and Cut | Verzweigen mit Schnittebenen | [Kapitel 6](#kap-milp) |
-| **KKT** | Karush–Kuhn–Tucker (Bedingungen) | Optimalitätsbedingungen bei Restriktionen | [Kapitel 11](#kap-qp-nlp) |
-| **SLSQP**`\index{SLSQP}`{=latex} | Sequential Least Squares Programming | sequentielle quadratische Optimierung | [Kapitel 11](#kap-qp-nlp) |
-| **RMT** | Random Matrix Theory | Zufallsmatrizentheorie`\index{Zufallsmatrizentheorie}`{=latex} | [Kapitel 18](#kap-finanzdaten) |
-| **SA** | Simulated Annealing | Simulierte Abkühlung — Metaheuristik`\index{Metaheuristik}`{=latex}, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | [Kapitel 9](#kap-metaheuristiken) |
-| **LNS** | Large Neighborhood Search | Große Nachbarschaftssuche: Teil der Lösung verwerfen, Rest exakt neu bauen | [Kapitel 9](#kap-metaheuristiken) |
-| **CG** | Column Generation`\index{Column Generation}`{=latex} | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | [Kapitel 10](#kap-dekomposition) |
-| **IIS** | Irreducible Infeasible Subset | Kleinste widersprüchliche Teilmenge von Bedingungen | [Anhang C](#anhang-fehlerdiagnose) |
-| **MSE** | Mean Squared Error | Mittlerer quadratischer Fehler — das Standardmaß für Prognosegüte | [Kapitel 15](#kap-prognose) |
+| **[KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}** | Karush–Kuhn–Tucker (Bedingungen) | Optimalitätsbedingungen bei Restriktionen | [Kapitel 11](#kap-qp-nlp) |
+| **[SLSQP](#gloss:slsqp){.glossar-link}**`\index{SLSQP}`{=latex} | Sequential Least Squares Programming | sequentielle quadratische Optimierung | [Kapitel 11](#kap-qp-nlp) |
+| **RMT** | Random Matrix Theory | [Zufallsmatrizentheorie](#gloss:zufallsmatrizentheorie){.glossar-link}`\index{Zufallsmatrizentheorie}`{=latex} | [Kapitel 18](#kap-finanzdaten) |
+| **SA** | Simulated Annealing | Simulierte Abkühlung — [Metaheuristik](#gloss:metaheuristik){.glossar-link}`\index{Metaheuristik}`{=latex}, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | [Kapitel 9](#kap-metaheuristiken) |
+| **[LNS](#gloss:large-neighborhood-search-lns){.glossar-link}** | Large Neighborhood Search | Große Nachbarschaftssuche: Teil der Lösung verwerfen, Rest exakt neu bauen | [Kapitel 9](#kap-metaheuristiken) |
+| **CG** | [Column Generation](#gloss:column-generation){.glossar-link}`\index{Column Generation}`{=latex} | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | [Kapitel 10](#kap-dekomposition) |
+| **[IIS](#gloss:iis-irreducible-infeasible-subset){.glossar-link}** | Irreducible Infeasible Subset | Kleinste widersprüchliche Teilmenge von Bedingungen | [Anhang C](#anhang-fehlerdiagnose) |
+| **[MSE](#gloss:mse){.glossar-link}** | Mean Squared Error | Mittlerer quadratischer Fehler — das Standardmaß für Prognosegüte | [Kapitel 15](#kap-prognose) |
### 2.2 Probleme mit Eigennamen
| Kürzel | Ausgeschrieben | Deutsch | Kapitel |
| --- | --- | --- | --- |
-| **TSP** | Traveling Salesperson Problem | Problem des Handlungsreisenden | [Kapitel 8](#kap-graphen) |
-| **VRP** | Vehicle Routing Problem | Tourenplanungsproblem | [Kapitel 8](#kap-graphen) |
+| **[TSP](#gloss:tsp-traveling-salesperson-problem){.glossar-link}** | Traveling Salesperson Problem | Problem des Handlungsreisenden | [Kapitel 8](#kap-graphen) |
+| **[VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link}** | Vehicle Routing Problem | Tourenplanungsproblem | [Kapitel 8](#kap-graphen) |
| **CVRP** | **C**apacitated VRP | VRP mit Fahrzeugkapazitäten | [Kapitel 8](#kap-graphen) |
| **VRPTW** | VRP with **T**ime **W**indows | VRP mit Zeitfenstern | [Kapitel 8](#kap-graphen) |
-| **CVRPTW**`\index{CVRPTW}`{=latex} | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | [Kapitel 8](#kap-graphen) |
+| **[CVRPTW](#gloss:cvrptw){.glossar-link}**`\index{CVRPTW}`{=latex} | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | [Kapitel 8](#kap-graphen) |
| **MCNFP** | Minimum-Cost Network Flow Problem | Kostenminimales Flussproblem | [Kapitel 8](#kap-graphen) |
| **MTZ** | Miller–Tucker–Zemlin (Formulierung) | Kurzzyklus-Eliminierung`\index{Subtour!Eliminierung}`{=latex} | [Kapitel 8](#kap-graphen) |
-| **JSSP** | Job-Shop Scheduling Problem | Werkstattfertigungs-Reihenfolgeproblem | [Kapitel 7](#kap-cpsat) |
+| **JSSP** | Job-Shop [Scheduling](#gloss:scheduling){.glossar-link} Problem | Werkstattfertigungs-Reihenfolgeproblem | [Kapitel 7](#kap-cpsat) |
### 2.3 Finanz- und Risikokennzahlen
| Kürzel | Ausgeschrieben | Deutsch / Bedeutung | Kapitel |
| --- | --- | --- | --- |
| **MPT** | Modern Portfolio Theory | Moderne Portfoliotheorie (Markowitz) | [Kapitel 19](#kap-markowitz) |
-| **GMV** | Global Minimum Variance (Portfolio) | Portfolio kleinstmöglicher Varianz | [Kapitel 19](#kap-markowitz) |
+| **[GMV](#gloss:global-minimum-variance-portfolio-gmv){.glossar-link}** | Global Minimum Variance (Portfolio) | Portfolio kleinstmöglicher Varianz | [Kapitel 19](#kap-markowitz) |
| **MVO** | Mean-Variance Optimization | Erwartungswert-Varianz-Optimierung | [Kapitel 19](#kap-markowitz) |
-| **SR** | Sharpe Ratio`\index{Sharpe-Ratio}`{=latex} | Überrendite je Einheit Volatilität | [Kapitel 19](#kap-markowitz) |
+| **SR** | [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}`\index{Sharpe-Ratio}`{=latex} | Überrendite je Einheit Volatilität | [Kapitel 19](#kap-markowitz) |
| **VaR** | Value at Risk | Verlustschwelle bei gegebener Wahrscheinlichkeit | [Kapitel 20](#kap-cvar) |
| **CVaR** | Conditional Value at Risk | mittlerer Verlust jenseits des VaR | [Kapitel 20](#kap-cvar) |
-| **ES** | Expected Shortfall`\index{Expected Shortfall}`{=latex} | anderes Wort für CVaR | [Kapitel 20](#kap-cvar) |
+| **ES** | [Expected Shortfall](#gloss:expected-shortfall){.glossar-link}`\index{Expected Shortfall}`{=latex} | anderes Wort für CVaR | [Kapitel 20](#kap-cvar) |
| **CAGR** | Compound Annual Growth Rate | durchschnittliche jährliche Wachstumsrate | [Kapitel 21](#kap-handelsmaschine) |
-| **MDD** | Maximum Drawdown`\index{Maximum Drawdown}`{=latex} | größter Rückgang vom Höchststand | [Kapitel 21](#kap-handelsmaschine) |
+| **MDD** | [Maximum Drawdown](#gloss:maximum-drawdown){.glossar-link}`\index{Maximum Drawdown}`{=latex} | größter Rückgang vom Höchststand | [Kapitel 21](#kap-handelsmaschine) |
| **LW** | Ledoit–Wolf (Shrinkage) | Schrumpfungsverfahren für Kovarianzmatrizen | [Kapitel 18](#kap-finanzdaten) |
| **p. a.** | *per annum* | pro Jahr | [Teil IV](#teil-anwendungen) |
| **bp** | Basispunkt | 0,01 Prozentpunkt; 15 bp = 0,15 % | [Kapitel 20](#kap-cvar), [Kapitel 21](#kap-handelsmaschine) |
@@ -677,23 +677,23 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| Kürzel | Ausgeschrieben | Was es ist | Lizenz |
| --- | --- | --- | --- |
-| **HiGHS**`\index{HiGHS}`{=latex} | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
+| **[HiGHS](#gloss:highs){.glossar-link}**`\index{HiGHS}`{=latex} | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
| **OR-Tools** | Google Operations Research Tools | Solver-Sammlung (CP-SAT, Routing, Wrapper) | Open Source (Apache 2.0) |
-| **CVXPY**`\index{CVXPY}`{=latex} | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
-| **SciPy** | **Sci**entific **Py**thon | Wissenschaftliches Python-Paket; `scipy.optimize` liefert LP- (über HiGHS), Zuordnungs- (`linear_sum_assignment`) und NLP-Solver (`minimize`) | Open Source (BSD) |
-| **GLOP** | **G**oogle **L**inear **Op**timizer | LP-Solver in OR-Tools | Open Source |
-| **SCIP**`\index{SCIP}`{=latex} | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
+| **[CVXPY](#gloss:cvxpy){.glossar-link}**`\index{CVXPY}`{=latex} | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
+| **[SciPy](#gloss:scipy){.glossar-link}** | **Sci**entific **Py**thon | Wissenschaftliches Python-Paket; `scipy.optimize` liefert LP- (über HiGHS), Zuordnungs- (`linear_sum_assignment`) und NLP-Solver (`minimize`) | Open Source (BSD) |
+| **[GLOP](#gloss:glop){.glossar-link}** | **G**oogle **L**inear **Op**timizer | LP-Solver in OR-Tools | Open Source |
+| **[SCIP](#gloss:scip){.glossar-link}**`\index{SCIP}`{=latex} | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
| **CBC** | **C**oin-or **B**ranch and **C**ut | MILP-Solver des COIN-OR-Projekts | Open Source |
| **GLPK** | GNU Linear Programming Kit | LP/MILP-Solver | Open Source (GPL) |
| **OSQP** | **O**perator **S**plitting **Q**uadratic **P**rogram | QP-Solver | Open Source |
| **ECOS** | Embedded Conic Solver | konischer Solver | Open Source |
| **SCS** | Splitting Conic Solver | konischer Solver | Open Source |
| **IPOPT** | **I**nterior **Po**int **Opt**imizer | NLP-Solver | Open Source (EPL) |
-| **Gurobi**`\index{Gurobi}`{=latex}, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
+| **[Gurobi](#gloss:gurobi){.glossar-link}**`\index{Gurobi}`{=latex}, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
| **API** | Application Programming Interface | Programmierschnittstelle | — |
| **DSL** | Domain-Specific Language | fachspezifische Sprache (hier: Modellierungssprache) | — |
| **CSR** | Compressed Sparse Row | Speicherformat für dünnbesetzte Matrizen | — |
-| **GIL** | Global Interpreter Lock | Pythons Sperre, die echte Parallelität von Python-Code verhindert — Solverbibliotheken in C++ geben sie frei ([Kapitel 23](#kap-testing)) | — |
+| **[GIL](#gloss:gil-global-interpreter-lock){.glossar-link}** | Global Interpreter Lock | Pythons Sperre, die echte Parallelität von Python-Code verhindert — Solverbibliotheken in C++ geben sie frei ([Kapitel 23](#kap-testing)) | — |
| **JSON** | JavaScript Object Notation | Textformat für strukturierte Daten; Austauschformat des Optimierungsdienstes | — |
| **RHS** | Right-Hand Side | rechte Seite einer (Un-)Gleichung, der Vektor $\mathbf{b}$ | — |
| **FFI** | Foreign Function Interface | Aufruf von C/C++-Code aus Python | — |
@@ -706,12 +706,12 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **FEASIBLE** | Zulässige Lösung gefunden, Optimalität nicht bewiesen | Zeitlimit erhöhen oder Gap akzeptieren |
| **INFEASIBLE** | Keine Lösung erfüllt alle Bedingungen | [Anhang C](#anhang-fehlerdiagnose): Diagnose durchführen |
| **UNBOUNDED** | Zielfunktion unbeschränkt verbesserbar | Fehlende Nebenbedingung — Modell prüfen |
-| **MIP-Gap**`\index{MIP-Gap}`{=latex} | Relativer Abstand beste Lösung ↔ beste Schranke | 1–2 % sind in der Praxis meist genug |
+| **[MIP-Gap](#gloss:mip-gap){.glossar-link}**`\index{MIP-Gap}`{=latex} | Relativer Abstand beste Lösung ↔ beste Schranke | 1–2 % sind in der Praxis meist genug |
| **Incumbent**`\index{Branch-and-Bound!Incumbent}`{=latex} | Beste bisher gefundene zulässige Lösung | Referenz für das Pruning |
-| **Relaxation**`\index{Relaxation}`{=latex} | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
+| **[Relaxation](#gloss:relaxation){.glossar-link}**`\index{Relaxation}`{=latex} | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
| **Warm Start** | Solver startet von bekannter Lösung | beschleunigt wiederholte Läufe |
| **Slack** | Schlupf, ungenutzte Kapazität | Slack = 0 → Engpass |
-| **Lookahead-Bias**`\index{Lookahead-Bias}`{=latex} | Nutzung von Daten aus der Zukunft | Backtest ist wertlos — [Kapitel 22](#kap-praxisfallen) |
+| **[Lookahead-Bias](#gloss:lookahead-bias){.glossar-link}**`\index{Lookahead-Bias}`{=latex} | Nutzung von Daten aus der Zukunft | [Backtest](#gloss:backtest){.glossar-link} ist wertlos — [Kapitel 22](#kap-praxisfallen) |
---
@@ -746,7 +746,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
>
> **Voraussetzungen:** Keine. Dies ist der Einstieg.
>
-> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, Parameter`\index{Parameter (Modell)}`{=latex}, Zielfunktion`\index{Zielfunktion}`{=latex} und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools`\index{OR-Tools}`{=latex} lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
+> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex}, [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} und Nebenbedingungen zerlegen, ein erstes Modell mit Google [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
>
> **Zeitbedarf:** ca. 3,5 Stunden inklusive Übungen.
>
@@ -779,7 +779,7 @@ Bevor wir über Theorie sprechen, lösen Sie ein echtes Problem. Kopieren Sie de
> produkt = {"Tisch": (240, 3.0, 6.0), "Stuhl": (60, 1.0, 1.0)} # DB, Stunden, m²
> vorrat = {"Montagestunden": 150, "Plattenmaterial": 240}
>
-> s = pywraplp.Solver.CreateSolver("GLOP")
+> s = pywraplp.[Solver](#gloss:solver){.glossar-link}.CreateSolver("[GLOP](#gloss:glop){.glossar-link}")
> x = {p: s.NumVar(0, s.infinity(), p) for p in produkt}
> s.Add(sum(x[p] * produkt[p][1] for p in produkt) <= vorrat["Montagestunden"])
> s.Add(sum(x[p] * produkt[p][2] for p in produkt) <= vorrat["Plattenmaterial"])
@@ -835,7 +835,7 @@ Drei Beispiele:
* Eine Klinik muss 200 Pflegekräfte einteilen — unter Berücksichtigung von 15 gesetzlichen Arbeitszeitregeln, Urlaubsanträgen, Qualifikationsstufen und Notfallreserven.
* Ein Fondsmanager muss 10 Millionen Euro so auf 50 Aktien aufteilen, dass bei einer erwarteten Mindestrendite von 8 % das Verlustrisiko im schlechtesten Marktszenario minimal bleibt.
-Ein naiver Ansatz — *Brute Force*`\index{Brute Force}`{=latex}, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
+Ein naiver Ansatz — *[Brute Force](#gloss:brute-force){.glossar-link}*`\index{Brute Force}`{=latex}, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.
> **📖 Definition: Operations Research**
>
@@ -893,7 +893,7 @@ stehen auf derselben Achse — deshalb kleben $n^2$ und $n^3$ am unteren Rand, w
Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nicht. Selbst eine Beschleunigung um den Faktor **eine Million** verschiebt die Grenze von $n=20$ nur auf etwa $n=24$. Gegen kombinatorisches Wachstum ist Hardware machtlos.
-Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem`\index{Zuordnungsproblem (1:1)}`{=latex} zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
+Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe [Zuordnungsproblem](#gloss:zuordnungsproblem){.glossar-link}`\index{Zuordnungsproblem (1:1)}`{=latex} zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.
```python
#!/usr/bin/env python3
@@ -1047,11 +1047,11 @@ Lesen Sie die Tabelle von oben nach unten: Bei $n = 4$ ist das Durchprobieren no
Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man die neu entwickelten Radaranlagen an der Küste optimal? Wie groß müssen Schiffskonvois sein, um U-Boot-Angriffe bei minimalem Geleitschutzaufwand abzuwehren?
-Wissenschaftler wie **George Dantzig**`\index{Dantzig, George}`{=latex}, **Patrick Blackett** und **John von Neumann** entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den **Simplex-Algorithmus**`\index{Simplex-Algorithmus}`{=latex} zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in [Kapitel 5](#kap-lp) selbst programmieren werden.
+Wissenschaftler wie **George Dantzig**`\index{Dantzig, George}`{=latex}, **Patrick Blackett** und **John von Neumann** entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den **[Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}**`\index{Simplex-Algorithmus}`{=latex} zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in [Kapitel 5](#kap-lp) selbst programmieren werden.
Heute treibt OR die Kernsysteme moderner Industrien an:
-* **Tech-Konzerne:** Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
+* **Tech-Konzerne:** Server-[Scheduling](#gloss:scheduling){.glossar-link}, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.
* **Luftfahrt & Logistik:** Crew-Scheduling, Flugzeugumlaufplanung, Paketlogistik (etwa das System *ORION* von UPS, das durch Routenoptimierung jährlich zweistellige Millionenbeträge an Treibstoff einspart).
* **Energie:** Kraftwerkseinsatzplanung, Netzausbau, Speicherbewirtschaftung.
* **Gesundheitswesen:** OP-Saal-Belegung, Dienstpläne, Rettungsmittel-Standorte.
@@ -1071,8 +1071,8 @@ Die **Stellschrauben**`\index{Entscheidungsvariable}`{=latex} des Modells: Werte
| Typ | Notation | Beispiel | Problemklasse |
| -------------- | ------------------ | -------------------------------------------------------- | ------------- |
-| Kontinuierlich | $x \in \mathbb{R}$ | Investitionsbetrag in Euro, Fördermenge in Litern | LP — leicht |
-| Ganzzahlig | $x \in \mathbb{Z}$ | Anzahl produzierter Maschinen, Anzahl Mitarbeiter | MILP — schwer |
+| Kontinuierlich | $x \in \mathbb{R}$ | Investitionsbetrag in Euro, Fördermenge in Litern | [LP](#gloss:lineare-programmierung-lp){.glossar-link} — leicht |
+| Ganzzahlig | $x \in \mathbb{Z}$ | Anzahl produzierter Maschinen, Anzahl Mitarbeiter | [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} — schwer |
| Binär | $x \in \{0, 1\}$ | „Aktie $i$ kaufen: ja/nein“, „Schicht zuweisen: ja/nein“ | MILP — schwer |
> **Die Leitfrage:** *Worüber darf ich überhaupt entscheiden?* Alles, was feststeht, ist kein Variable, sondern Parameter. Die häufigste Ursache für unbrauchbare Modelle ist eine falsch gewählte Variablendefinition — nicht ein falscher Solver.
@@ -1081,7 +1081,7 @@ Die **Stellschrauben**`\index{Entscheidungsvariable}`{=latex} des Modells: Werte
Die **unveränderlichen Fakten**`\index{Parameter (Modell)}`{=latex} der Realität: historische Renditen, Maschinenstundensätze, Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen.
-> **Die Leitfrage:** *Was ist gegeben und wird nicht entschieden?* Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. [Kapitel 18](#kap-finanzdaten) zeigt am Beispiel der Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex}, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
+> **Die Leitfrage:** *Was ist gegeben und wird nicht entschieden?* Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. [Kapitel 18](#kap-finanzdaten) zeigt am Beispiel der [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex}, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.
### Baustein 3 — Zielfunktion ($f(x)$)
@@ -1111,7 +1111,7 @@ $$
**Harte Nebenbedingungen (*hard constraints*)**`\index{Nebenbedingung!hart}`{=latex} sind zwingend: gesetzliche Ruhezeit $\ge 11$ Stunden, Summe der Portfoliogewichte $= 100\,\%$, maximales Verlustrisiko $\le 5\,\%$. Wird eine einzige harte Bedingung verletzt, ist die Lösung mathematisch **unzulässig** (*infeasible*)`\index{Infeasibility}`{=latex} — sie existiert für den Solver schlicht nicht.
-**Weiche Nebenbedingungen (*soft constraints*)**`\index{Nebenbedingung!weich}`{=latex} sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über **Strafkosten**`\index{Strafkosten}`{=latex} (*penalties*) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
+**Weiche Nebenbedingungen (*soft constraints*)**`\index{Nebenbedingung!weich}`{=latex} sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über **[Strafkosten](#gloss:strafkosten){.glossar-link}**`\index{Strafkosten}`{=latex} (*penalties*) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.
> **🎯 Merksatz** Harte Bedingung = „darf nicht“. Weiche Bedingung = „soll möglichst nicht, sonst kostet es X“. Die Entscheidung zwischen beiden ist eine der folgenreichsten im ganzen Modell: Zu viele harte Bedingungen erzeugen unlösbare Modelle ([Kapitel 22](#kap-praxisfallen)), zu wenige erzeugen Lösungen, die niemand akzeptiert.
@@ -1477,8 +1477,8 @@ Das Ergebnis deckt sich mit der Handrechnung *Das Optimum zu Fuß finden* — ei
> | ---------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | Konstanten oben | alle Parameter an einer Stelle | Nie Zahlen im Modellcode verstreuen — sonst findet man sie beim Ändern nicht wieder |
> | `NewIntVar(0, 100, ...)` | ganzzahlige Variable mit weiten Grenzen | Grenzen müssen endlich sein; 100 ist bewusst unkritisch groß |
-> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis`\index{Schattenpreis}`{=latex} abfragen |
-> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status`\index{Solver-Status}`{=latex} auswerten |
+> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} abfragen |
+> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten [Solver-Status](#gloss:solver-status){.glossar-link}`\index{Solver-Status}`{=latex} auswerten |
> | `assert ...` | Selbstkontrolle nach dem Lösen | Kostet nichts und fängt Modellierungsfehler, die der Solver nicht bemerken kann |
> | Vergleich mit naiver Strategie | quantifiziert den Nutzen | Die Frage „Was bringt die Optimierung überhaupt?“ kommt in jedem Projekt — beantworten Sie sie ungefragt |
@@ -1510,7 +1510,7 @@ In den allermeisten Betrieben liegen die Zahlen, die ein Optimierungsmodell brau
> | „Zielzelle“ + Max/Min | `solver.Maximize(...)` | Statt einer Formel in einer Zelle ein Ausdruck im Code. |
> | „Nebenbedingungen“-Liste | `solver.Add(...)` | Eine Zeile je Regel — oder eine Schleife für 10 000 Regeln. |
> | „Ganzzahlig“ / „Binär“ | `NewIntVar(...)` / `NewBoolVar(...)` | Gleiche Bedeutung, gleiche Konsequenz für die Laufzeit. |
-> | Simplex-LP / GRG / Evolutionär | GLOP / SLSQP`\index{SLSQP}`{=latex} / CP-SAT`\index{CP-SAT}`{=latex} | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ([Kapitel 3](#kap-oekosystem)). |
+> | Simplex-LP / GRG / Evolutionär | GLOP / [SLSQP](#gloss:slsqp){.glossar-link}`\index{SLSQP}`{=latex} / [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ([Kapitel 3](#kap-oekosystem)). |
> | „Sensitivitätsbericht“ | `bedingung.dual_value()` | Der Schattenpreis — [Kapitel 5](#kap-lp) erklärt, was er bedeutet. |
> | 200 Zeilen sind das Limit | keine praktische Grenze | Der Excel-Solver ist auf 200 veränderbare Zellen begrenzt. |
@@ -1680,7 +1680,7 @@ Ergebnis geschrieben nach: produktionsmix_ergebnis.xlsx
**Aufgabe 1.4 ⭐⭐ — Modell lesen.** Gegeben sei das Modell $$\max\ 3x_1 + 5x_2 \quad \text{u. d. N.} \quad x_1 \le 4,\ \ 2x_2 \le 12,\ \ 3x_1 + 2x_2 \le 18,\ \ x_1, x_2 \ge 0.$$ (a) Benennen Sie die vier Bausteine. (b) Ist $(2, 6)$ zulässig? Ist $(4, 3)$ zulässig? Berechnen Sie jeweils $Z$. (c) Finden Sie durch Probieren die beste ganzzahlige Lösung.
-**Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren.** Setzen Sie das Bäckerei-Problem der Handrechnung *Zerlegen Sie dieses Problem* mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit `assert`-Anweisungen.
+**Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren.** Setzen Sie das Bäckerei-Problem der Handrechnung *Zerlegen Sie dieses Problem* mit [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit `assert`-Anweisungen.
**Aufgabe 1.6 ⭐⭐⭐ — Sensitivität durch Ausprobieren.** Erweitern Sie `Bot_Allokation.py` so, dass es das Modell in einer Schleife für RAM-Kapazitäten von 54 bis 72 GB (in Schritten von 2) löst und eine Tabelle `RAM | x_A | x_B | Gewinn | Gewinnzuwachs pro zusätzlichem GB` ausgibt. (a) Ab welcher RAM-Menge steigt der Gewinn nicht mehr? Warum? (b) Was sagt Ihnen der „Gewinnzuwachs pro GB“ intuitiv? (Der Begriff dafür — *Schattenpreis* — folgt in [Kapitel 5](#kap-lp).)
@@ -1760,13 +1760,13 @@ Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht
## 1.13 Zusammenfassung {#sec:einfuehrung-zusammenfassung}
* **Operations Research ist präskriptiv:** Es beantwortet nicht, was war oder was kommt, sondern was zu tun ist.
-* **Kombinatorische Explosion**`\index{Kombinatorische Explosion}`{=latex} **ist eine Wand, keine Steigung.** Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
+* **[Kombinatorische Explosion](#gloss:kombinatorische-explosion){.glossar-link}**`\index{Kombinatorische Explosion}`{=latex} **ist eine Wand, keine Steigung.** Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.
* **Jedes Modell besteht aus vier Bausteinen:** Entscheidungsvariablen, Parameter, Zielfunktion, Nebenbedingungen. Diese Zerlegung ist die eigentliche Arbeit; die Solverwahl ergibt sich danach fast von selbst.
* **Harte und weiche Bedingungen sind eine Entwurfsentscheidung** mit großen Folgen für Lösbarkeit und Akzeptanz.
* **Ihre Daten liegen schon in Excel.** `pandas.read_excel()` holt sie ab, der Solver rechnet, `to_excel()` gibt das Ergebnis im gewohnten Format zurück. Der Gewinn gegenüber einer Solver-Mappe ist nicht Rechenleistung, sondern Nachvollziehbarkeit: versionierbar, testbar, hundertmal in einer Schleife ausführbar.
* **Prüfen Sie jedes Ergebnis** — gegen eine Handrechnung, gegen `assert`-Anweisungen und gegen eine naive Vergleichsstrategie. Ein unerwartet gutes Ergebnis ist ein Fehlerverdacht, keine gute Nachricht.
-**Ausblick.** [Kapitel 2](#kap-fundament) liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: **Konvexität**`\index{Konvexität}`{=latex}.
+**Ausblick.** [Kapitel 2](#kap-fundament) liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: **[Konvexität](#gloss:konvexitaet){.glossar-link}**`\index{Konvexität}`{=latex}.
---
@@ -1775,7 +1775,7 @@ Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: Konvexität.
+> **Worum geht es?** Um die Sprache, in der Optimierungsmodelle aufgeschrieben werden, und um die eine Eigenschaft, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: [Konvexität](#gloss:konvexitaet){.glossar-link}.
>
> **Voraussetzungen:** [Kapitel 1](#kap-einfuehrung). Lineare Algebra wird hier von Grund auf wiederholt.
>
@@ -1795,7 +1795,7 @@ Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht
## 2.1 In 5 Minuten gelöst {#sec:fundament-schnellstart}
-Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
+Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne [Solver](#gloss:solver){.glossar-link}, mit acht Zeilen [NumPy](#gloss:numpy){.glossar-link}.
> **🚀 In 5 Minuten gelöst: Das Optimum sitzt immer in einer Ecke**
>
@@ -1832,9 +1832,9 @@ Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen
> Ecke (0.0, 0.0) -> Z = 0.00
> ```
-**Das Optimum lautet $(0, 8)$ mit $Z = 40$** — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der **Fundamentalsatz der linearen Optimierung**`\index{Fundamentalsatz der linearen Optimierung}`{=latex}, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
+**Das Optimum lautet $(0, 8)$ mit $Z = 40$** — und Sie haben es gefunden, ohne einen einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der **[Fundamentalsatz der linearen Optimierung](#gloss:fundamentalsatz-der-linearen-optimierung){.glossar-link}**`\index{Fundamentalsatz der linearen Optimierung}`{=latex}, und er ist der Grund, warum sich lineare Programme überhaupt zuverlässig lösen lassen.
-**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes Polyeder**`\index{Polyeder}`{=latex} ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion`\index{Zielfunktion}`{=latex} darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der Simplex-Algorithmus`\index{Simplex-Algorithmus}`{=latex} in [Kapitel 5](#kap-lp) tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
+**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes [Polyeder](#gloss:polyeder){.glossar-link}**`\index{Polyeder}`{=latex} ist — ein Vielflächner ohne Diagonalen nach innen — und weil eine lineare [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} darauf ihren größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses Kapitel. Der [Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}`\index{Simplex-Algorithmus}`{=latex} in [Kapitel 5](#kap-lp) tut im Kern nichts anderes als das Programm oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu Ecke bergauf.
> **⚠️ Und wo ist der Haken?** Bei zwei Variablen gibt es 6 Eckenkandidaten, bei 50 Variablen und 50 Bedingungen sind es $\binom{100}{50} \approx 10^{29}$. Der Fundamentalsatz sagt uns *wo* wir suchen müssen — nicht, dass die Suche leicht wird.
@@ -1849,7 +1849,7 @@ Nach diesem Kapitel können Sie …
3. … den Fundamentalsatz der linearen Optimierung anwenden, um Kandidaten für das Optimum zu finden, ohne alles durchzuprobieren.
4. … prüfen, ob eine Menge`\index{Konvexität!Menge}`{=latex} bzw. eine Funktion`\index{Konvexität!Funktion}`{=latex} konvex ist, und begründen, warum das für die Lösbarkeit entscheidend ist.
5. … einen zweidimensionalen Lösungsraum mit `matplotlib` zeichnen und daraus die optimale Ecke ablesen.
-6. … die **Konditionszahl**`\index{Konditionszahl}`{=latex} $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit Ruiz-Equilibrierung`\index{Ruiz-Equilibrierung}`{=latex} wieder rechenbar machen.
+6. … die **[Konditionszahl](#gloss:konditionszahl){.glossar-link}**`\index{Konditionszahl}`{=latex} $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein schlecht skaliertes Modell mit [Ruiz-Equilibrierung](#gloss:ruiz-equilibrierung){.glossar-link}`\index{Ruiz-Equilibrierung}`{=latex} wieder rechenbar machen.
7. … erklären, warum ein Solver-Ergebnis von `0.99999998` niemals mit `int()` in eine ganze Zahl verwandelt werden darf.
---
@@ -1864,7 +1864,7 @@ Die lineare Algebra löst dieses Problem, indem sie **viele gleichartige Zahlen
Sei $n$ die Anzahl der Entscheidungsvariablen und $m$ die Anzahl der Nebenbedingungen.
-**Entscheidungsvektor**`\index{Entscheidungsvektor}`{=latex} — was wir festlegen:
+**[Entscheidungsvektor](#gloss:entscheidungsvektor){.glossar-link}**`\index{Entscheidungsvektor}`{=latex} — was wir festlegen:
$$
\mathbf{x} = \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} \in \mathbb{R}^n
@@ -1876,7 +1876,7 @@ $$
\mathbf{c} = \begin{pmatrix} c_1 \\ c_2 \\ \vdots \\ c_n \end{pmatrix} \in \mathbb{R}^n
$$
-**Zielfunktion als Skalarprodukt`\index{Skalarprodukt}`{=latex}:**
+**Zielfunktion als [Skalarprodukt](#gloss:skalarprodukt){.glossar-link}`\index{Skalarprodukt}`{=latex}:**
$$
f(\mathbf{x}) = \mathbf{c}^\top \mathbf{x} = \sum_{j=1}^n c_j x_j
@@ -1889,7 +1889,7 @@ $$
>
> **Ohne Formel gesagt:** „Nimm von jedem Produkt die hergestellte Menge mal den Gewinn pro Stück und zähle alles zusammen.“ Im Bot-Beispiel: $\mathbf{c} = (150, 250)^\top$, $\mathbf{x} = (7, 5)^\top$, also $\mathbf{c}^\top\mathbf{x} = 150 \cdot 7 + 250 \cdot 5 = 2300$.
-**Technologiematrix**`\index{Technologiematrix}`{=latex} — wer verbraucht wie viel wovon:
+**[Technologiematrix](#gloss:technologiematrix){.glossar-link}**`\index{Technologiematrix}`{=latex} — wer verbraucht wie viel wovon:
$$
\mathbf{A} = \begin{pmatrix}
@@ -1904,7 +1904,7 @@ $$
>
> Im Bot-Beispiel: $$\mathbf{A} = \begin{pmatrix} 2 & 5 \\ 4 & 6 \\ 1 & 0 \end{pmatrix} \begin{matrix} \leftarrow \text{vCPU} \\ \leftarrow \text{RAM} \\ \leftarrow \text{Liquidität} \end{matrix}$$ Die erste Zeile $(2, 5)$ heißt: Ein A-Bot braucht 2 vCPUs, ein B-Bot braucht 5. Die dritte Zeile $(1, 0)$ heißt: Die Liquiditätsgrenze zählt nur A-Bots, B-Bots gar nicht.
-**Kapazitätsvektor (rechte Seite)**`\index{Kapazitätsvektor}`{=latex}:
+**[Kapazitätsvektor](#gloss:kapazitaetsvektor){.glossar-link} (rechte Seite)**`\index{Kapazitätsvektor}`{=latex}:
$$
\mathbf{b} = \begin{pmatrix} b_1 \\ b_2 \\ \vdots \\ b_m \end{pmatrix} \in \mathbb{R}^m
@@ -2131,11 +2131,11 @@ $$
| Problemtyp | Konvex? | Konsequenz |
| --- | --- | --- |
-| Lineares Programm (LP) | **ja** | Globales Optimum, Sekunden bis Minuten selbst bei $10^6$ Variablen |
+| Lineares Programm ([LP](#gloss:lineare-programmierung-lp){.glossar-link}) | **ja** | Globales Optimum, Sekunden bis Minuten selbst bei $10^6$ Variablen |
| Quadratisches Programm mit $\mathbf{P} \succeq 0$ | **ja** | Globales Optimum, effizient ([Kapitel 11](#kap-qp-nlp), [Kapitel 19](#kap-markowitz)) |
-| Ganzzahlige Variablen (MILP) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — NP-schwer`\index{NP-schwer}`{=latex} ([Kapitel 6](#kap-milp)) |
-| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} ist ein typischer Auslöser (siehe [Abschnitt 11.5](#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize)) |
-| Allgemeines NLP mit beliebigen Funktionen | meist **nein** | `scipy.optimize` liefert nur ein lokales Optimum, abhängig vom Startpunkt |
+| Ganzzahlige Variablen ([MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — [NP-schwer](#gloss:np-schwer){.glossar-link}`\index{NP-schwer}`{=latex} ([Kapitel 6](#kap-milp)) |
+| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} ist ein typischer Auslöser (siehe [Abschnitt 11.5](#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize)) |
+| Allgemeines [NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link} mit beliebigen Funktionen | meist **nein** | `scipy.optimize` liefert nur ein lokales Optimum, abhängig vom Startpunkt |
### Konvexität sichtbar machen
@@ -2505,7 +2505,7 @@ mit Diagonalmatrizen $\mathbf{D}_r$ (Zeilen) und $\mathbf{D}_c$ (Spalten). Das *
Wirtschaftlich passiert dabei nichts: Eine Zeilenskalierung heißt „diese Nebenbedingung in einer anderen Einheit messen“, eine Spaltenskalierung „diese Variable in einer anderen Einheit messen“. Die Lösung rechnet man mit $\mathbf{x} = \mathbf{D}_c\,\tilde{\mathbf{x}}$ zurück.
-> **🎯 Merksatz** Jeder ernsthafte Solver skaliert intern selbst — HiGHS`\index{HiGHS}`{=latex}, Gurobi`\index{Gurobi}`{=latex}, CP-SAT`\index{CP-SAT}`{=latex} alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
+> **🎯 Merksatz** Jeder ernsthafte Solver skaliert intern selbst — [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex}, [Gurobi](#gloss:gurobi){.glossar-link}`\index{Gurobi}`{=latex}, [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} alle. Verlassen Sie sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das etwas über Ihr Modell, nicht über den Solver.
### Das Experiment
@@ -2834,7 +2834,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
>
> ```python
> import numpy as np
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> c = np.array([3.0, 5.0])
> A = np.array([[1.0, 3.0], # spaltenweise abgetippt statt zeilenweise
@@ -2907,7 +2907,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
>
> **Voraussetzungen:** [Kapitel 1](#kap-einfuehrung) und [Kapitel 2](#kap-fundament).
>
-> **Danach können Sie:** Für ein gegebenes Problem begründet einen Solver wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
+> **Danach können Sie:** Für ein gegebenes Problem begründet einen [Solver](#gloss:solver){.glossar-link} wählen, dasselbe Modell in verschiedenen Bibliotheken formulieren, die Ergebnisse gegeneinander prüfen — und messen, ob Ihre Laufzeit überhaupt im Solver entsteht oder schon davor.
>
> **Zeitbedarf:** ca. 4 Stunden.
>
@@ -2923,7 +2923,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
## 3.1 In 5 Minuten gelöst {#sec:oekosystem-schnellstart}
-> **🚀 In 5 Minuten gelöst: Ein LP ohne jede Installation**
+> **🚀 In 5 Minuten gelöst: Ein [LP](#gloss:lineare-programmierung-lp){.glossar-link} ohne jede Installation**
>
> Ein Futtermittelhersteller mischt zwei Rohstoffe zu möglichst geringen Kosten. Jeder Kilogramm Mischung muss mindestens 20 g Protein und 5 g Fett enthalten.
>
@@ -2932,10 +2932,10 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
> | Weizenschrot | 0,42 € | 12 g | 2 g |
> | Sojaschrot | 0,88 € | 44 g | 15 g |
>
-> Sie brauchen dafür **keine** zusätzliche Bibliothek — SciPy genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
+> Sie brauchen dafür **keine** zusätzliche Bibliothek — [SciPy](#gloss:scipy){.glossar-link} genügt, und SciPy ist in jeder wissenschaftlichen Python-Installation schon da:
>
> ```python
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> # linprog MINIMIERT und kennt nur "<=". Mindestgehalte werden also negiert.
> res = linprog(c=[0.42, 0.88], # Kosten je kg
@@ -2951,20 +2951,20 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
> **Ausgabe:**
>
> ```
-> Optimization terminated successfully. (HiGHS Status 7: Optimal)
+> Optimization terminated successfully. ([HiGHS](#gloss:highs){.glossar-link} Status 7: Optimal)
> Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
> ```
-**Sie haben soeben HiGHS`\index{HiGHS}`{=latex} benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter CVXPY`\index{CVXPY}`{=latex} und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die dünnste denkbare Hülle darum.
+**Sie haben soeben HiGHS`\index{HiGHS}`{=latex} benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die dünnste denkbare Hülle darum.
Das ist die zentrale Botschaft dieses Kapitels: **Modellierungsschicht und Solver sind zwei verschiedene Dinge.** Die Bibliothek, in der Sie Ihr Modell hinschreiben, bestimmt, wie angenehm die Arbeit ist. Der Solver dahinter bestimmt, wie schnell gerechnet wird. Beide lassen sich unabhängig voneinander tauschen — und genau davon handelt der Rest des Kapitels.
> **⚠️ Zwei Stolpersteine stecken schon in diesen sechs Zeilen**
>
-> * `linprog` **minimiert immer**. Wer maximieren will, negiert die Zielfunktion`\index{Zielfunktion}`{=latex} — und darf nicht vergessen, das Ergebnis zurückzudrehen.
+> * `linprog` **minimiert immer**. Wer maximieren will, negiert die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} — und darf nicht vergessen, das Ergebnis zurückzudrehen.
> * `linprog` kennt **nur `<=`**. Ein Mindestgehalt „$\ge 20$“ wird zu „$-12x_1 - 44x_2 \le -20$“. Ein Vorzeichenfehler an dieser Stelle liefert eine perfekt aussehende Lösung, die das Gegenteil des Gewollten erfüllt.
>
-> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo`\index{Pyomo}`{=latex} benutzt, in der `>=` einfach `>=` heißt. Das ist ein Hauptgrund, warum es sie gibt.
+> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder [Pyomo](#gloss:pyomo){.glossar-link}`\index{Pyomo}`{=latex} benutzt, in der `>=` einfach `>=` heißt. Das ist ein Hauptgrund, warum es sie gibt.
---
@@ -2977,8 +2977,8 @@ Nach diesem Kapitel können Sie …
3. … dasselbe lineare Programm in `scipy.optimize`, `highspy`, `ortools` und `cvxpy` formulieren.
4. … Ergebnisse verschiedener Solver gegeneinander validieren (*Cross-Check*).
5. … einschätzen, wann sich der Aufwand einer Low-Level-Schnittstelle lohnt und wann nicht.
-6. … Pyomo und Linopy`\index{Linopy}`{=latex} einordnen und begründen, wann sich der Umstieg auf sie lohnt.
-7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars`\index{Polars}`{=latex} vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
+6. … Pyomo und [Linopy](#gloss:linopy){.glossar-link}`\index{Linopy}`{=latex} einordnen und begründen, wann sich der Umstieg auf sie lohnt.
+7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit [NumPy](#gloss:numpy){.glossar-link} oder [Polars](#gloss:polars){.glossar-link}`\index{Polars}`{=latex} vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
---
@@ -2987,13 +2987,13 @@ Nach diesem Kapitel können Sie …
Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbst. Man nutzt eine **zweischichtige Architektur**`\index{Zweischichtige Architektur}`{=latex}:
1. **Modellierungsschicht (*Frontend*, DSL):** Python-Bibliotheken, mit denen Entscheidungsvariablen, Zielfunktion und Nebenbedingungen in mathematiknaher Syntax formuliert werden. Hier arbeiten Sie.
-2. **Solver-Schicht (*Backend*, Engine):** Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen (Dual Simplex`\index{Dual Simplex}`{=latex}, Interior-Point`\index{Interior-Point-Verfahren}`{=latex}, Branch-and-Cut`\index{Branch-and-Cut}`{=latex}, CDCL-SAT-Suche`\index{CDCL}`{=latex}).
+2. **Solver-Schicht (*Backend*, Engine):** Hochoptimierte C++- oder Fortran-Bibliotheken, die das Modell in Matrixstrukturen übersetzen und mit spezialisierten Algorithmen lösen ([Dual Simplex](#gloss:dual-simplex){.glossar-link}`\index{Dual Simplex}`{=latex}, Interior-Point`\index{Interior-Point-Verfahren}`{=latex}, [Branch-and-Cut](#gloss:branch-and-cut){.glossar-link}`\index{Branch-and-Cut}`{=latex}, [CDCL](#gloss:cdcl){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link}-Suche`\index{CDCL}`{=latex}).

> **🎯 Merksatz** Die Modellierungsschicht bestimmt, wie angenehm Sie arbeiten. Die Solver-Schicht bestimmt, wie schnell gerechnet wird. Beides ist entkoppelt — man kann dieselbe CVXPY-Formulierung mit fünf verschiedenen Solvern lösen.
-**Warum diese Trennung nützlich ist:** Ein CVXPY`\index{CVXPY}`{=latex}-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi`\index{Gurobi}`{=latex} — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
+**Warum diese Trennung nützlich ist:** Ein CVXPY`\index{CVXPY}`{=latex}-Modell, das heute mit dem freien Solver Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen [Gurobi](#gloss:gurobi){.glossar-link}`\index{Gurobi}`{=latex} — man tauscht ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
---
@@ -3002,17 +3002,17 @@ Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbs
| Bibliothek | Stärken & Problemklassen | Wann einsetzen? | Typische Backends |
| --- | --- | --- | --- |
| `scipy.optimize` | Standard-LPs, kontinuierliche nichtlineare Optimierung, Wurzelsuche | Für elementare LPs und schnelle Prototypen ohne zusätzliche schwere Abhängigkeiten | HiGHS (`highs-ds`, `highs-ipm`) |
-| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing (VRP/TSP), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | CP-SAT`\index{CP-SAT}`{=latex}, GLOP, Routing Engine |
-| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, CVaR, $L_1$-Transaktionskosten`\index{Transaktionskosten}`{=latex} | Clarabel, OSQP, ECOS, SCS, HiGHS |
-| `highspy` | Direkte Solver-Steuerung für LP/MILP, maximale Kontrolle | Wenn Sie Modelle wiederholt und schnell aufbauen oder Solver-Details steuern müssen | HiGHS |
-| `Pyomo` | Universelle Modellierungssprache (LP, MILP, MINLP`\index{MINLP}`{=latex}) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, SCIP`\index{SCIP}`{=latex}, IPOPT |
+| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing ([VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link}/[TSP](#gloss:tsp-traveling-salesperson-problem){.glossar-link}), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}, [GLOP](#gloss:glop){.glossar-link}, Routing Engine |
+| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link}, $L_1$-[Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} | Clarabel, OSQP, ECOS, SCS, HiGHS |
+| `highspy` | Direkte Solver-Steuerung für LP/[MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}, maximale Kontrolle | Wenn Sie Modelle wiederholt und schnell aufbauen oder Solver-Details steuern müssen | HiGHS |
+| `Pyomo` | Universelle Modellierungssprache (LP, MILP, [MINLP](#gloss:minlp){.glossar-link}`\index{MINLP}`{=latex}) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, [SCIP](#gloss:scip){.glossar-link}`\index{SCIP}`{=latex}, IPOPT |
| `Linopy` | LP/MILP über beschriftete Arrays (`xarray`) | Modelle mit zehntausenden gleichartigen Nebenbedingungen (Energie, Netze, Zeitreihen) | HiGHS, GLPK, CBC, Gurobi |
### Die Entscheidung in drei Fragen
Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:
-> **Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen?** → **Ja:** OR-Tools`\index{OR-Tools}`{=latex} (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → **Nein:** weiter zu Frage 2.
+> **Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen?** → **Ja:** [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} ([CP](#gloss:constraint-programming-cp){.glossar-link}-SAT) bei Zuweisung/[Scheduling](#gloss:scheduling){.glossar-link}, oder MILP über HiGHS bei ökonomischen Fixkostenmodellen. → **Nein:** weiter zu Frage 2.
>
> **Frage 2: Ist die Zielfunktion linear?** → **Ja:** `scipy.optimize.linprog` (klein) oder `highspy` (groß, wiederholt). → **Nein, aber konvex** (Quadrate, Normen, `log`, `exp` in konvexer Kombination): **CVXPY**. → **Nein und nicht konvex:** `scipy.optimize.minimize` — im Bewusstsein, dass nur ein lokales Optimum herauskommt.
>
@@ -3170,7 +3170,7 @@ Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion`\index{Kon
> **⚠️ `max_tasks_per_child=1` ist nicht optional** Ein Pool ohne diese Angabe ist der **Normalfall** — er soll seine Arbeiter ja wiederverwenden. Wer die Isolation über einen Pool herstellt und das vergisst, hat einen Prozesswechsel programmiert, aber keine Isolation gewonnen: Die zweite Aufgabe landet im selben Interpreter wie die erste. Der Absturz kommt dann nicht beim ersten Solver, sondern beim zweiten — und sieht aus wie ein Problem des zweiten.
-Denselben Aufbau verwenden `Solverwechsel_CPSAT_HiGHS.py` ([Kapitel 22](#kap-praxisfallen)) und `Benchmark_Skalierung.py` ([Kapitel 23](#kap-testing)). Dort wandern zusätzlich **Datenobjekte** über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-DTO keinen Solver kennen ([Abschnitt 22.6](#sec:praxisfallen-or-kern)).
+Denselben Aufbau verwenden `Solverwechsel_CPSAT_HiGHS.py` ([Kapitel 22](#kap-praxisfallen)) und `Benchmark_Skalierung.py` ([Kapitel 23](#kap-testing)). Dort wandern zusätzlich **Datenobjekte** über die Prozessgrenze statt Zeichenketten — möglich, weil Domänenmodell und Lösungs-[DTO](#gloss:dto-data-transfer-object){.glossar-link} keinen Solver kennen ([Abschnitt 22.6](#sec:praxisfallen-or-kern)).
```python
#!/usr/bin/env python3
@@ -3348,7 +3348,7 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
> | Ansatz | Zeilen für das Modell | Charakter |
> | --- | --- | --- |
> | `linprog` | 3 | Matrizen direkt übergeben. Kürzeste Variante, aber man muss selbst negieren und die Matrixform`\index{Matrixform}`{=latex} von Hand herstellen. |
-> | `highspy` | ~15 | Alles explizit, inklusive CSR-Format`\index{CSR-Format}`{=latex} der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
+> | `highspy` | ~15 | Alles explizit, inklusive [CSR-Format](#gloss:csr-format){.glossar-link}`\index{CSR-Format}`{=latex} der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
> | `cvxpy` | 4 | Liest sich wie die mathematische Formulierung. Prüft zusätzlich automatisch, ob das Problem konvex ist. Höchster Startaufwand pro Lauf (Kompilierung des Ausdrucksbaums). |
> | `ortools`/GLOP | ~6 | Bedingungen einzeln mit `Add()` — gut lesbar bei wenigen, mühsam bei vielen Restriktionen. |
>
@@ -3375,7 +3375,7 @@ Bei kleinen Modellen wirkt das umständlich. Bei realen Modellen mit 100 000 Var
> * **Vergessen, dass `linprog` minimiert.** Der häufigste Fehler überhaupt. Symptom: Der „optimale“ Gewinn ist erstaunlich niedrig oder null.
> * **CVXPY für ein nicht-konvexes Problem verwenden.** CVXPY lehnt das ab mit `DCPError: Problem does not follow DCP rules`. Das ist ein **Feature**, keine Einschränkung: Der Fehler sagt Ihnen, dass Ihre Formulierung keine Optimalitätsgarantie hätte.
> * **CP-SAT mit kontinuierlichen Variablen füttern.** CP-SAT kennt nur ganze Zahlen. Wer Euro-Beträge modelliert, rechnet in Cent (Ganzzahl) — oder nimmt einen LP-Solver.
-> * **Für jeden Lauf ein neues Modell bauen.** Bei 60 Backtest-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. `highspy` und CVXPY-`Parameter` erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe [Kapitel 19](#kap-markowitz) und [Kapitel 21](#kap-handelsmaschine)).
+> * **Für jeden Lauf ein neues Modell bauen.** Bei 60 [Backtest](#gloss:backtest){.glossar-link}-Rebalancings kostet das Aufbauen mehr Zeit als das Lösen. `highspy` und CVXPY-`Parameter` erlauben es, das Modell einmal zu bauen und nur Daten zu tauschen (siehe [Kapitel 19](#kap-markowitz) und [Kapitel 21](#kap-handelsmaschine)).
---
@@ -3385,11 +3385,11 @@ Bei kleinen Modellen wirkt das umständlich. Bei realen Modellen mit 100 000 Var
| --- | --- | --- |
| Einmaliges kleines LP, Prototyp | `scipy.optimize.linprog` | Keine zusätzliche Abhängigkeit, 3 Zeilen |
| Portfolio, Risiko, alles Konvexe | **CVXPY** | Lesbarkeit + automatische Konvexitätsprüfung |
-| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | Globale Constraints`\index{Globale Constraints}`{=latex}, Konfliktlernen |
+| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | [Globale Constraints](#gloss:globale-constraints){.glossar-link}`\index{Globale Constraints}`{=latex}, Konfliktlernen |
| Fahrzeugtouren | **OR-Tools Routing** | Fertige Metaheuristiken, jahrzehntelang optimiert |
| MILP mit Fixkosten, Kardinalität | `highspy` oder CP-SAT | Branch-and-Cut auf ökonomischer Struktur |
| 50 000+ Variablen, wiederholte Läufe | `highspy` oder Pyomo | Modellaufbau wird sonst zum Engpass |
-| Nichtkonvexes NLP | `scipy.optimize.minimize` | Bewusst mit mehreren Startpunkten arbeiten |
+| Nichtkonvexes [NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link} | `scipy.optimize.minimize` | Bewusst mit mehreren Startpunkten arbeiten |
> **🎯 Merksatz** Wählen Sie den Solver nach der **Struktur des Modells**, nicht nach Gewohnheit. Ein Zuweisungsproblem in CVXPY zu quälen oder ein Portfolio mit CP-SAT nachzubauen kostet Laufzeit und Nerven — und meist auch Lösungsqualität.
@@ -3973,7 +3973,7 @@ Lösezeit (C++). Werte aus der Messung oben. Erzeugt von `bilder_04/erzeuge_lauf
> **🐛 Finde den Denkfehler: Der Solver, der angeblich dreimal schneller ist**
>
-> Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem`\index{Zuordnungsproblem (1:1)}`{=latex} und schreibt ins Protokoll: *„CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“* Gemessen wurde so:
+> Ein Team vergleicht zwei Bibliotheken für sein [Zuordnungsproblem](#gloss:zuordnungsproblem){.glossar-link}`\index{Zuordnungsproblem (1:1)}`{=latex} und schreibt ins Protokoll: *„CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“* Gemessen wurde so:
>
> ```python
> import time
@@ -4005,11 +4005,11 @@ Lösezeit (C++). Werte aus der Messung oben. Erzeugt von `bilder_04/erzeuge_lauf
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-oekosystem).
>
-> **1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt?** (a) CVXPY — es prüft die Konvexität`\index{Konvexität}`{=latex} automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) `scipy.optimize.minimize` — es kann beliebige Zielfunktionen.
+> **1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt?** (a) CVXPY — es prüft die [Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex} automatisch. (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet. (c) `scipy.optimize.minimize` — es kann beliebige Zielfunktionen.
>
> **2. Ihr Modell mit 80 000 Variablen braucht 40 Sekunden, davon 32 Sekunden bis zum ersten Solver-Aufruf. Was ist die wirksamste Maßnahme?** (a) Einen kommerziellen Solver lizenzieren. (b) Das Zeitlimit des Solvers heraufsetzen. (c) Den Modellaufbau vektorisieren — der Solver ist mit 8 Sekunden gar nicht der Engpass.
>
-> **3. Was haben `scipy.optimize.linprog`, `highspy`, CVXPY (mit Standardeinstellung) und Pyomo (mit `appsi_highs`) gemeinsam?** (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus`\index{Simplex-Algorithmus}`{=latex} in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
+> **3. Was haben `scipy.optimize.linprog`, `highspy`, CVXPY (mit Standardeinstellung) und Pyomo (mit `appsi_highs`) gemeinsam?** (a) Sie implementieren jeweils einen eigenen [Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}`\index{Simplex-Algorithmus}`{=latex} in Python. (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung ist dieselbe, nur die Schreibweise unterscheidet sich. (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.
---
@@ -4044,7 +4044,7 @@ Lösezeit (C++). Werte aus der Messung oben. Erzeugt von `bilder_04/erzeuge_lauf
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
+> **Worum geht es?** Um den Schritt, der vor jedem [Solver](#gloss:solver){.glossar-link} kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.
>
> **Voraussetzungen:** [Kapitel 1](#kap-einfuehrung) (die vier Bausteine eines Modells). Kein Solverwissen nötig; das ganze Kapitel kommt mit einer einzigen `linprog`-Zeile aus.
>
@@ -4075,7 +4075,7 @@ Lösezeit (C++). Werte aus der Messung oben. Erzeugt von `bilder_04/erzeuge_lauf
>
> ```python
> import numpy as np
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> # Auftrag: A B C D E
> stunden = np.array([ 60, 30, 30, 25, 15], dtype=float)
@@ -4109,7 +4109,7 @@ Niemand in der Besprechung hätte behauptet, Umsatz sei wichtiger als Ertrag. De
> **🎯 Merksatz** Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.
-**Warum funktioniert das?** Weil eine Zielfunktion`\index{Zielfunktion}`{=latex} eine **vollständige** Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig", sondern **wertlos**. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
+**Warum funktioniert das?** Weil eine [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} eine **vollständige** Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig", sondern **wertlos**. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.
---
@@ -4542,7 +4542,7 @@ Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.
### Teil 3: Hart oder weich
-Dieselbe Regel als Strafkosten`\index{Strafkosten}`{=latex} je abgelehntem Stammkundenauftrag:
+Dieselbe Regel als [Strafkosten](#gloss:strafkosten){.glossar-link}`\index{Strafkosten}`{=latex} je abgelehntem Stammkundenauftrag:
| Strafe je Auftrag | Deckungsbeitrag | abgelehnte Stammaufträge |
| ---: | ---: | ---: |
@@ -4610,26 +4610,26 @@ Die Tabelle liest sich von links nach rechts: Was in der Besprechung **gesagt**
| Was gesagt wird | Was gemeint ist | Baustein |
| --- | --- | --- |
| „Wir haben nur 400 Stunden." | eine Obergrenze, die nicht überschritten werden darf | harte Kapazitätsgrenze $\le b$ ([Kapitel 5](#kap-lp)) |
-| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | Budgetlimit`\index{Budgetlimit}`{=latex} (B27) |
+| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | [Budgetlimit](#gloss:budgetlimit){.glossar-link}`\index{Budgetlimit}`{=latex} (B27) |
| „Möglichst nicht über 40 Stunden." | eine Grenze, die im Notfall gebrochen werden darf | weiche Grenze mit Strafkosten (B8) |
| „Wir können nur ganze Kisten liefern." | die Menge`\index{Konvexität!Menge}`{=latex} ist nicht teilbar | Ganzzahligkeitsbedingung ([Kapitel 6](#kap-milp)) |
-| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | Aktivierungsschalter`\index{Aktivierungsschalter (Modellierungsmuster)}`{=latex} (B1) |
+| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | [Aktivierungsschalter](#gloss:aktivierungsschalter){.glossar-link}`\index{Aktivierungsschalter (Modellierungsmuster)}`{=latex} (B1) |
| „Entweder gar nicht oder mindestens 50." | kein Betrieb im Kleinen | semikontinuierliche Variable (B2) |
-| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | Implikation`\index{Implikation (Modellierungsmuster)}`{=latex} (B3) |
+| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | [Implikation](#gloss:implikation){.glossar-link}`\index{Implikation (Modellierungsmuster)}`{=latex} (B3) |
| „Höchstens drei Positionen im Depot." | eine Obergrenze auf die **Anzahl**, nicht die Menge | Kardinalität (B6) |
-| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | Verhältnis-Bedingung`\index{Verhältnis-Bedingung}`{=latex} (B12) |
+| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | [Verhältnis-Bedingung](#gloss:verhaeltnis-bedingung){.glossar-link}`\index{Verhältnis-Bedingung}`{=latex} (B12) |
| „Jeder Auftrag genau einem Mitarbeiter." | eine vollständige, eindeutige Zuordnung | Zuordnung 1:1 (B23) |
-| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | Nichtüberlappung`\index{Nichtüberlappung}`{=latex} (B14) |
-| „B kann erst nach A." | eine Reihenfolge | Vorrangbeziehung`\index{Vorrangbeziehung}`{=latex} (B13) |
+| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | [Nichtüberlappung](#gloss:nichtueberlappung){.glossar-link}`\index{Nichtüberlappung}`{=latex} (B14) |
+| „B kann erst nach A." | eine Reihenfolge | [Vorrangbeziehung](#gloss:vorrangbeziehung){.glossar-link}`\index{Vorrangbeziehung}`{=latex} (B13) |
| „Höchstens fünf Tage am Stück." | eine Bedingung über ein **gleitendes Fenster** | gleitendes Fenster (B16) |
-| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | Umrüstkosten`\index{Umrüstkosten (Modellierungsmuster)}`{=latex} (B17), Rüstzeit`\index{Rüstzeit}`{=latex} als Kapazität (B26) |
-| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | Flusserhaltung`\index{Flusserhaltung}`{=latex} (B22) |
+| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | [Umrüstkosten](#gloss:umruestkosten){.glossar-link}`\index{Umrüstkosten (Modellierungsmuster)}`{=latex} (B17), [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex} als Kapazität (B26) |
+| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | [Flusserhaltung](#gloss:flusserhaltung){.glossar-link}`\index{Flusserhaltung}`{=latex} (B22) |
| „Notfalls brechen wir die Regel." | die Bedingung ist weich, aber teuer | Schlupfvariablen (B18) |
| „Erst Termintreue, dann Kosten." | eine Rangfolge zwischen Zielen | hierarchische Ziele (B19) |
| „Es soll gerecht zugehen." | den Schlechtestgestellten verbessern | Min/Max in der Zielfunktion (B11) |
-| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | Schattenpreis`\index{Schattenpreis}`{=latex} ([Kapitel 5](#kap-lp), [Abschnitt 22.4](#sec:praxisfallen-attribution)) |
-| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | Chance Constraint`\index{Chance Constraint}`{=latex} ([Abschnitt 12.7](#sec:unsicherheit-chance-constraints)) |
-| „Wir sichern uns gegen Schätzfehler`\index{Schätzfehler}`{=latex} ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug`\index{Robuste Optimierung!Worst-Case-Abzug}`{=latex} (B21) |
+| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} ([Kapitel 5](#kap-lp), [Abschnitt 22.4](#sec:praxisfallen-attribution)) |
+| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | [Chance Constraint](#gloss:chance-constraint){.glossar-link}`\index{Chance Constraint}`{=latex} ([Abschnitt 12.7](#sec:unsicherheit-chance-constraints)) |
+| „Wir sichern uns gegen [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug`\index{Robuste Optimierung!Worst-Case-Abzug}`{=latex} (B21) |
### Fünf Wendungen, bei denen die Übersetzung eine Entscheidung erzwingt
@@ -4655,9 +4655,9 @@ Dieselbe Barriere gibt es rückwärts. Diese Sätze haben sich bewährt, wenn ei
| bindende Bedingung mit Dualwert 0 | „Die Regel berührt den Plan, kostet uns aber nichts — dort zu verhandeln bringt nichts." |
| `INFEASIBLE` | „Ihre Regeln widersprechen sich." **Nicht:** „Der Computer schafft es nicht." |
| Optimalitätslücke von 2 % | „Besser als das hier geht es höchstens um 2 % — und die liegen unter unserer Datenungenauigkeit." |
-| CVaR | „Wenn es schiefgeht, verlieren wir im Mittel so viel." (Der VaR sagt nur, **ob** es schiefgeht.) |
-| EVPI | „Selbst eine perfekte Prognose wäre uns höchstens so viel wert." |
-| Relaxation`\index{Relaxation}`{=latex} | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
+| [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} | „Wenn es schiefgeht, verlieren wir im Mittel so viel." (Der [VaR](#gloss:value-at-risk-var){.glossar-link} sagt nur, **ob** es schiefgeht.) |
+| [EVPI](#gloss:evpi-expected-value-of-perfect-information){.glossar-link} | „Selbst eine perfekte Prognose wäre uns höchstens so viel wert." |
+| [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
| Chance Constraint | „Der Plan hält in 95 von 100 Fällen — und der nächste Prozentpunkt kostet …" |
---
@@ -4733,15 +4733,15 @@ Dieselbe Barriere gibt es rückwärts. Diese Sätze haben sich bewährt, wenn ei
# Synthese Teil I — Grundlagen auf einen Blick
-Vier Kapitel, eine Frage: **Was muss vorliegen, bevor ein Solver überhaupt sinnvoll gestartet werden kann?** Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
+Vier Kapitel, eine Frage: **Was muss vorliegen, bevor ein [Solver](#gloss:solver){.glossar-link} überhaupt sinnvoll gestartet werden kann?** Die Antwort ist unbequem, weil das meiste davon nichts mit Programmieren zu tun hat.
## Was welches Kapitel klärt
| Frage | Kapitel | Was Sie danach können |
| --- | --- | --- |
-| Was ist überhaupt ein Optimierungsmodell? | [Kapitel 1](#kap-einfuehrung) | Die vier Bausteine benennen — Variablen, Zielfunktion`\index{Zielfunktion}`{=latex}, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
-| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | [Kapitel 2](#kap-fundament) | Konvexität`\index{Konvexität}`{=latex} beurteilen, den zulässigen Bereich als Polyeder`\index{Polyeder}`{=latex} sehen, schlechte Konditionierung erkennen und beheben |
-| Welche Bibliothek, und warum vertragen sie sich nicht? | [Kapitel 3](#kap-oekosystem) | Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS`\index{HiGHS}`{=latex}-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
+| Was ist überhaupt ein Optimierungsmodell? | [Kapitel 1](#kap-einfuehrung) | Die vier Bausteine benennen — Variablen, [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
+| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | [Kapitel 2](#kap-fundament) | [Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex} beurteilen, den zulässigen Bereich als [Polyeder](#gloss:polyeder){.glossar-link}`\index{Polyeder}`{=latex} sehen, schlechte Konditionierung erkennen und beheben |
+| Welche Bibliothek, und warum vertragen sie sich nicht? | [Kapitel 3](#kap-oekosystem) | Solver, Bindings und Modellierungsschichten unterscheiden — und den [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex}-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
| Wie wird aus einem Satz ein Modell? | [Kapitel 4](#kap-modellierung) | Die vier Fragen stellen, harte von weichen Regeln trennen, einen Besprechungssatz übersetzen ([Abschnitt 4.6](#sec:modellierung-glossar)) |
## Die Reihenfolge ist die Aussage
@@ -4769,9 +4769,9 @@ Vier Kapitel, vier Werkzeugkästen. Welcher zu Ihrem Problem passt, entscheiden

-> **🎯 Merksatz zum Diagramm** Die erste Frage ist die wichtigste — und sie lautet **nicht** „welcher Solver ist der schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein Zuordnungsproblem`\index{Zuordnungsproblem (1:1)}`{=latex} braucht keinen MILP-Solver, sondern eine Zeile `linear_sum_assignment`. Ein Dienstplan gehört nicht in ein LP, sondern zu CP-SAT`\index{CP-SAT}`{=latex}. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
+> **🎯 Merksatz zum Diagramm** Die erste Frage ist die wichtigste — und sie lautet **nicht** „welcher [Solver](#gloss:solver){.glossar-link} ist der schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein [Zuordnungsproblem](#gloss:zuordnungsproblem){.glossar-link}`\index{Zuordnungsproblem (1:1)}`{=latex} braucht keinen [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}-Solver, sondern eine Zeile `linear_sum_assignment`. Ein Dienstplan gehört nicht in ein [LP](#gloss:lineare-programmierung-lp){.glossar-link}, sondern zu [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}. Wer die Struktur erkennt, hat die halbe Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
-Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion`\index{Zielfunktion}`{=latex}, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist [Teil III](#teil-nichtlinear) der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
+Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}, unsichere Daten, Entscheidungen über mehrere Zeitstufen —, dann ist [Teil III](#teil-nichtlinear) der richtige Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
---
@@ -4779,11 +4779,11 @@ Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion`\in
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um das Kernverfahren des Operations Research. Wir bauen den Simplex-Algorithmus`\index{Simplex-Algorithmus}`{=latex} selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher **Schattenpreise** kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
+> **Worum geht es?** Um das Kernverfahren des Operations Research. Wir bauen den [Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}`\index{Simplex-Algorithmus}`{=latex} selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher **Schattenpreise** kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
>
-> **Voraussetzungen:** [Kapitel 2](#kap-fundament) (Matrixform`\index{Matrixform}`{=latex}, Polyeder, Ecken).
+> **Voraussetzungen:** [Kapitel 2](#kap-fundament) (Matrixform`\index{Matrixform}`{=latex}, [Polyeder](#gloss:polyeder){.glossar-link}, Ecken).
>
-> **Danach können Sie:** Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein Schattenpreis`\index{Schattenpreis}`{=latex} eine Entscheidung **nicht** tragen darf.
+> **Danach können Sie:** Ein LP in Standardform bringen, den Simplex von Hand rechnen, Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und erkennen, wann ein [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} eine Entscheidung **nicht** tragen darf.
>
> **Zeitbedarf:** ca. 7 Stunden — das umfangreichste Kapitel von [Teil II](#teil-kernverfahren).
>
@@ -4813,7 +4813,7 @@ Bisher hat ein Solver Ihnen gesagt, *was* Sie tun sollen. Jetzt sagt er Ihnen zu
> Der Betriebsrat hat 20 Überstunden genehmigt. **In welchem Bereich sollen sie eingesetzt werden?**
>
> ```python
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> # Deckungsbeitrag je Stueck (negiert, weil linprog minimiert)
> c = [-90, -140]
@@ -4857,9 +4857,9 @@ Nach diesem Kapitel können Sie …
2. … eine Simplex-Iteration von Hand rechnen: Pivotspalte`\index{Simplex-Algorithmus!Pivotspalte}`{=latex}, Pivotzeile, Basistausch.
3. … erklären, warum das Verfahren terminiert und warum es das Optimum findet.
4. … das duale Problem zu einem gegebenen primalen aufstellen.
-5. … **Schattenpreise**`\index{Schattenpreis}`{=latex} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle`\index{Schattenpreis!Vorzeichenfalle}`{=latex}, die einer der häufigsten Fehler bei der Sensitivitätsanalyse`\index{Sensitivitätsanalyse}`{=latex} ist.
+5. … **Schattenpreise**`\index{Schattenpreis}`{=latex} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle`\index{Schattenpreis!Vorzeichenfalle}`{=latex}, die einer der häufigsten Fehler bei der [Sensitivitätsanalyse](#gloss:sensitivitaetsanalyse){.glossar-link}`\index{Sensitivitätsanalyse}`{=latex} ist.
6. … aus einer Sensitivitätsanalyse eine begründete Kaufentscheidung ableiten.
-7. … ein Modell auf **Entartung**`\index{Entartung}`{=latex} prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
+7. … ein Modell auf **[Entartung](#gloss:entartung){.glossar-link}**`\index{Entartung}`{=latex} prüfen und statt eines nicht eindeutigen Schattenpreises dessen belastbare Spanne berechnen.
8. … erklären, warum Zulässigkeit immer gegen eine Toleranz und nie mit `== 0` geprüft wird.
---
@@ -4896,7 +4896,7 @@ $$
Der von George `\index{Dantzig, George}`{=latex}Dantzig 1947 formulierte `\index{Simplex-Algorithmus}`{=latex}Simplex-Algorithmus nutzt den Fundamentalsatz aus [Kapitel 2](#kap-fundament): **Das Optimum liegt in einer Ecke.** Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.

+(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr [Gradient](#gloss:gradient){.glossar-link}`\index{Gradient}`{=latex} $\mathbf{c}$. Erzeugt von `bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
**Was Sie in der Abbildung sehen.** Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder`\index{Polyeder}`{=latex} es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
@@ -5183,7 +5183,7 @@ Die Ausgabe reproduziert die Simplex-Handrechnung exakt — inklusive der Schatt
>
> * **Negative rechte Seite.** $-3x_1 - 2x_2 \le -12$ (aus einer „$\ge$“-Bedingung entstanden) macht den Ursprung unzulässig. Dann braucht man Phase 1 mit künstlichen Variablen. Dieser Solver meldet das jetzt ehrlich.
> * **Zyklen bei Entartung.** Wenn mehrere Quotienten gleich sind, kann der Simplex theoretisch endlos kreisen. In der Praxis extrem selten; Bland's Regel verhindert es beweisbar.
-> * **Den Simplex selbst produktiv einsetzen.** Diese Implementierung ist ein Lehrmodell. HiGHS`\index{HiGHS}`{=latex} ist um Größenordnungen schneller und numerisch stabiler.
+> * **Den Simplex selbst produktiv einsetzen.** Diese Implementierung ist ein Lehrmodell. [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex} ist um Größenordnungen schneller und numerisch stabiler.
---
@@ -5571,7 +5571,7 @@ Der Zukauf für 18 €/h ist hochprofitabel: Jede zusätzliche Prüfstunde bring
>
> 1. **`schattenpreise = -ergebnis.ineqlin.marginals`** — die eigentliche Korrektur.
> 2. **Numerische Gegenprobe:** Das Modell wird mit $b_3 + 1$ neu gelöst; der gemessene Zuwachs muss dem Schattenpreis entsprechen. Diese Prüfung ist unabhängig von jeder Vorzeichenkonvention und deshalb der zuverlässigste Test überhaupt.
-> 3. **Komplementärer Schlupf`\index{Komplementärer Schlupf}`{=latex}:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
+> 3. **[Komplementärer Schlupf](#gloss:komplementaerer-schlupf){.glossar-link}`\index{Komplementärer Schlupf}`{=latex}:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
>
> **Übernehmen Sie dieses Muster in eigene Projekte.** Der Aufwand ist eine Handvoll Zeilen, der Nutzen ist die Gewissheit, dass Ihre Handlungsempfehlung nicht das Gegenteil des Richtigen sagt.
@@ -5602,7 +5602,7 @@ Das ist keine theoretische Sorge. Dasselbe Modell, zwei Verfahren desselben Solv
| Verfahren | Schattenpreise | Was das Management daraus liest |
| --- | --- | --- |
-| Dual Simplex`\index{Dual Simplex}`{=latex} | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
+| [Dual Simplex](#gloss:dual-simplex){.glossar-link}`\index{Dual Simplex}`{=latex} | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
| Innere-Punkte-Verfahren | $(0;\ 0;\ 1{,}00)$ | „Die Prüfung ist der Engpass, Fräse und Schleiferei sind wertlos.“ |
**Beide Zeilen sind mathematisch korrekt.** Sie widersprechen sich trotzdem vollständig.
@@ -5615,7 +5615,7 @@ Solver rechnen mit endlicher Genauigkeit und brechen ab, sobald ihre eigene Tole
| Toleranz | Was sie steuert | Typischer Standardwert |
| --- | --- | --- |
-| **Primale Zulässigkeit** | Wie weit darf $\mathbf{A}\mathbf{x}$ die Schranke $\mathbf{b}$ überschreiten? | $10^{-7}$ (HiGHS), $10^{-6}$ (CP-SAT) |
+| **Primale Zulässigkeit** | Wie weit darf $\mathbf{A}\mathbf{x}$ die Schranke $\mathbf{b}$ überschreiten? | $10^{-7}$ (HiGHS), $10^{-6}$ ([CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link}) |
| **Duale Zulässigkeit** (Optimalität) | Wie weit dürfen die reduzierten Kosten das falsche Vorzeichen haben? | $10^{-7}$ |
| **Ganzzahligkeit** | Wie weit darf eine Ganzzahlvariable von der nächsten ganzen Zahl abweichen? | $10^{-6}$ ([Kapitel 6](#kap-milp)) |
@@ -5904,7 +5904,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
**Aufgabe 5.3 ⭐⭐ — Simplex von Hand.** Lösen Sie mit dem Tableau-Verfahren vollständig von Hand: $$\max\ 5x_1 + 4x_2 \quad \text{u. d. N.}\quad 6x_1+4x_2 \le 24,\quad x_1+2x_2 \le 6,\quad x_1,x_2\ge0$$ Geben Sie jedes Zwischentableau an und lesen Sie am Ende Lösung **und** Schattenpreise ab. Prüfen Sie mit `scipy.optimize.linprog`.
-**Aufgabe 5.4 ⭐⭐ — Duales Problem`\index{Duales Problem}`{=latex} aufstellen.** Stellen Sie zum Modell aus der Aufgabe *Simplex von Hand* das duale Problem auf, lösen Sie es mit `linprog` und weisen Sie den starken Dualitätssatz numerisch nach.
+**Aufgabe 5.4 ⭐⭐ — [Duales Problem](#gloss:duales-problem){.glossar-link}`\index{Duales Problem}`{=latex} aufstellen.** Stellen Sie zum Modell aus der Aufgabe *Simplex von Hand* das duale Problem auf, lösen Sie es mit `linprog` und weisen Sie den starken Dualitätssatz numerisch nach.
**Aufgabe 5.5 ⭐⭐ — Unbeschränktheit erkennen.** Was liefert `SimplexTableauSolver` für $\max x_1 + x_2$ u. d. N. $x_1 - x_2 \le 5$, $x_1, x_2 \ge 0$? Erklären Sie die Fehlermeldung geometrisch.
@@ -5979,7 +5979,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
* **Schattenpreise** beantworten die Managementfrage schlechthin: Wo ist der Engpass, und was ist seine Beseitigung wert?
* **Vorzeichen prüfen!** Nach einer Negation der Zielfunktion müssen die Dualwerte zurücknegiert werden. Die numerische Gegenprobe („Kapazität um 1 erhöhen und neu lösen“) ist die sicherste Kontrolle.
-**Ausblick.** [Kapitel 6](#kap-milp) bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen Branch-and-Bound`\index{Branch-and-Bound}`{=latex} kennen.
+**Ausblick.** [Kapitel 6](#kap-milp) bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau, sondern grundsätzlich falsch ist — und lernen [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}`\index{Branch-and-Bound}`{=latex} kennen.
---
@@ -5990,9 +5990,9 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
>
> **Worum geht es?** Um Entscheidungen, die nicht teilbar sind: ganze Maschinen, Ja/Nein, „höchstens fünf davon“. Wir zeigen mit einem konkreten Gegenbeispiel, warum Runden **falsch** ist, und lernen die Modellierungstricks, mit denen sich logische Regeln in lineare Ungleichungen übersetzen lassen.
>
-> **Voraussetzungen:** [Kapitel 5](#kap-lp) (LP, Relaxation`\index{Relaxation}`{=latex}, Schranken).
+> **Voraussetzungen:** [Kapitel 5](#kap-lp) ([LP](#gloss:lineare-programmierung-lp){.glossar-link}, [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex}, Schranken).
>
-> **Danach können Sie:** Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein MILP mit CP-SAT`\index{CP-SAT}`{=latex} oder HiGHS lösen, den MIP-Gap`\index{MIP-Gap}`{=latex} als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
+> **Danach können Sie:** Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen modellieren, ein [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} mit [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} oder [HiGHS](#gloss:highs){.glossar-link} lösen, den [MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex} als Zusage an den Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil das Big-M zu groß gewählt war.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
>
@@ -6035,7 +6035,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
> nb = LinearConstraint(A, [100, -np.inf, -np.inf, -np.inf], [100, 0, 0, 0])
> grenzen = Bounds([0] * 6, list(kap) + [1, 1, 1])
>
-> for name, ganzzahlig in [("LP-Relaxation", [0] * 6), ("MILP", [0, 0, 0, 1, 1, 1])]:
+> for name, ganzzahlig in [("[LP-Relaxation](#gloss:lp-relaxation){.glossar-link}", [0] * 6), ("MILP", [0, 0, 0, 1, 1, 1])]:
> r = milp(c=np.concatenate([var, fix]), constraints=[nb],
> integrality=ganzzahlig, bounds=grenzen)
> print(f"{name:14} {r.fun:7.2f} EUR Mengen {np.round(r.x[:3], 1)} "
@@ -6063,7 +6063,7 @@ Runden führt hier also in beide Richtungen in die Irre: einmal in die Unzuläss
> **🎯 Merksatz** Die LP-Relaxation ist keine ungefähre Antwort, die man nur noch glattziehen muss. Sie ist eine Antwort auf eine **andere Frage** — nämlich die, bei der man Lager auch zu 14,3 % eröffnen darf. Ihr Wert liegt nicht in ihrer Lösung, sondern in ihrer **Schranke**: Weniger als 724,14 € kann die richtige Antwort nicht kosten.
-Genau diese Schranke ist der Hebel, mit dem Branch-and-Bound arbeitet. Wie, zeigt der Abschnitt [Abschnitt 6.4](#sec:milp-branch-and-bound).
+Genau diese Schranke ist der Hebel, mit dem [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link} arbeitet. Wie, zeigt der Abschnitt [Abschnitt 6.4](#sec:milp-branch-and-bound).
---
@@ -6077,8 +6077,8 @@ Nach diesem Kapitel können Sie …
4. … begründen, warum $M$ so klein wie möglich gewählt werden muss.
5. … ein Portfolioproblem mit Ordergebühren und Höchstzahl an Positionen lösen.
6. … **Incumbent`\index{Branch-and-Bound!Incumbent}`{=latex}, Schranke und MIP-Gap** unterscheiden und aus einem abgebrochenen Solverlauf eine belastbare Aussage ableiten.
-7. … alle Solver-Statusfälle explizit behandeln, statt `OPTIMAL` vorauszusetzen.
-8. … einen **Warm-Start**`\index{Warm-Start}`{=latex} setzen — und messen, ob er auf Ihrer Problemklasse überhaupt etwas bringt.
+7. … alle [Solver](#gloss:solver){.glossar-link}-Statusfälle explizit behandeln, statt `OPTIMAL` vorauszusetzen.
+8. … einen **[Warm-Start](#gloss:warm-start){.glossar-link}**`\index{Warm-Start}`{=latex} setzen — und messen, ob er auf Ihrer Problemklasse überhaupt etwas bringt.
---
@@ -6090,7 +6090,7 @@ In der realen Welt sind viele Entscheidungen nicht teilbar. Man kann nicht 0,47
Der verlockende Gedanke lautet: *„Wir lösen das Problem kontinuierlich und runden.“* Eine verbreitete, aber unbelegte Behauptung dazu lautet, man verliere dadurch „oft 20–50 % des Gewinns“. Rechnen wir es nach, statt es zu behaupten.
-{.glossar-link}`\index{Zielfunktion}`{=latex} läuft parallel zur
Restriktion, also ist die ganze Kante optimal. Aufrunden auf $(1;1)$ führt aus dem zulässigen Bereich heraus, Abrunden auf $(0;0)$ auf den Wert null. Rechts die Messung aus `Runden_Gegenbeispiel.py` über je 200 Zufallsinstanzen. Erzeugt von `bilder_04/erzeuge_runden.py`.](bilder_04/kap_milp_runden.svg)
> **✏️ Handrechnung 6.1: Ein Gegenbeispiel, das Sie im Kopf prüfen können**
@@ -6238,7 +6238,7 @@ Loesung eine Nebenbedingung verletzt. 'Verlust' = Abstand zum exakten Optimum.
## 6.4 Branch-and-Bound {#sec:milp-branch-and-bound}
-MILP-Probleme sind **NP-schwer**`\index{NP-schwer}`{=latex}. Der Standardansatz moderner Solver (`\index{HiGHS}`{=latex}HiGHS, `\index{SCIP}`{=latex}SCIP, `\index{Gurobi}`{=latex}Gurobi) heißt **Branch-and-Cut**`\index{Branch-and-Cut}`{=latex} — Branch-and-Bound`\index{Branch-and-Bound}`{=latex} plus Schnittebenen.
+MILP-Probleme sind **[NP-schwer](#gloss:np-schwer){.glossar-link}**`\index{NP-schwer}`{=latex}. Der Standardansatz moderner Solver (`\index{HiGHS}`{=latex}HiGHS, `\index{SCIP}`{=latex}[SCIP](#gloss:scip){.glossar-link}, `\index{Gurobi}`{=latex}[Gurobi](#gloss:gurobi){.glossar-link}) heißt **[Branch-and-Cut](#gloss:branch-and-cut){.glossar-link}**`\index{Branch-and-Cut}`{=latex} — Branch-and-Bound`\index{Branch-and-Bound}`{=latex} plus Schnittebenen.

@@ -6463,7 +6463,7 @@ Drei Beobachtungen, die den Kern des Kapitels illustrieren:
**Szenario.** Ein Investor verteilt $100\,000\,\text{€}$ auf sechs Anlageklassen.
* Jede Transaktion kostet **50 € Fixgebühr**, unabhängig vom Betrag.
-* Höchstens **3 Positionen** gleichzeitig (*Kardinalitätsbeschränkung*`\index{Kardinalitätsbeschränkung}`{=latex}).
+* Höchstens **3 Positionen** gleichzeitig (*[Kardinalitätsbeschränkung](#gloss:kardinalitaetsbeschraenkung){.glossar-link}*`\index{Kardinalitätsbeschränkung}`{=latex}).
* Wird eine Position eröffnet, dann mit **mindestens 10 000 €** (Mindestordergröße).
* Höchstens **40 000 €** pro Position.
@@ -6746,11 +6746,11 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
> | `zeile[i]=1; zeile[N+i]=-MAX_POSITION` | Muster 1: $x_i - U y_i \le 0$. **$M = U = 40\,000$** — die kleinstmögliche gültige Wahl |
> | `zeile[i]=-1; zeile[N+i]=MIN_POSITION` | Muster 5 unten: $L y_i - x_i \le 0$ |
> | `integrality=concat([zeros(N), ones(N)])` | nur die $y$ sind ganzzahlig — das ist das „mixed“ in MILP |
-> | `status = status_von_scipy(ergebnis)` | übersetzt den SciPy-Rückgabewert in die gemeinsame Sprache aus [Abschnitt 22.6](#sec:praxisfallen-or-kern) — die drei `if`-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt |
+> | `status = status_von_scipy(ergebnis)` | übersetzt den [SciPy](#gloss:scipy){.glossar-link}-Rückgabewert in die gemeinsame Sprache aus [Abschnitt 22.6](#sec:praxisfallen-or-kern) — die drei `if`-Zweige darunter behandeln Modellfehler, Abbruch ohne Lösung und „zulässig, aber unbewiesen“ getrennt |
> | `schranke=-ergebnis.mip_dual_bound` | die zweite Zahl aus [Abschnitt 6.8](#sec:milp-gap). Erst mit ihr kann das `Loesung`-Objekt den Gap ausrechnen — und der Bericht sagt `Gap: 0.00%`, also *beweisbar* optimal |
> | `pruefe_portfolio(...)` | prüft **jede** modellierte Regel einzeln nach und liefert eine Liste von Beanstandungen |
>
-> **Warum das Ergebnis wirtschaftlich Sinn ergibt:** Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die Diversifikation`\index{Diversifikation}`{=latex} das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
+> **Warum das Ergebnis wirtschaftlich Sinn ergibt:** Der Solver wählt die drei renditestärksten Anlagen (13 %, 11 %, 8 %) und füllt sie in dieser Reihenfolge bis zur Obergrenze von 40 000 €. Die drittbeste Position (EU-Aktien) erhält nur den Rest von 20 000 € — sie liegt über der Mindestordergröße von 10 000 €, ist also zulässig. Ohne Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position kostet uns 1 950 € pro Jahr — ist uns die [Diversifikation](#gloss:diversifikation){.glossar-link}`\index{Diversifikation}`{=latex} das wert?“ Das ist eine beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
---
@@ -7258,7 +7258,7 @@ Hier wirkt der Hinweis: Faktor 2,2 bzw. 1,4. Beachten Sie aber, dass der Faktor
> * **`if status == OPTIMAL: ... else: return None`.** Wirft eine Lösung weg, die 9 % vom Optimum entfernt und damit vollkommen brauchbar ist. Werten Sie den Gap aus, nicht nur den Status.
> * **Zeitlimit ohne Gap-Auswertung.** Ein Limit erzwingt ein Ende, sagt aber nichts über die Qualität. Ohne den Gap wissen Sie nicht, ob Sie 0,1 % oder 60 % danebenliegen.
> * **Warm-Start als Selbstverständlichkeit.** Messen Sie ihn — auf Ihrer Instanzklasse, gegen den kalten Lauf. In der Hälfte der Fälle bringt er nichts.
-> * **Einen Hinweis für eine unzulässige Lösung geben.** CP-SAT und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
+> * **Einen Hinweis für eine unzulässige Lösung geben.** [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} und HiGHS verwerfen ihn dann stillschweigend. Prüfen Sie Ihre Startlösung vorher auf Zulässigkeit — sonst messen Sie einen Effekt, den es gar nicht gibt.
> **🎯 Merksatz** Ein Warm-Start kann das Ergebnis nie verfälschen — im schlimmsten Fall kostet er Zeit. Genau deshalb darf man ihn ausprobieren. Aber man muss ihn auch **messen**, statt an ihn zu glauben.
@@ -7595,7 +7595,7 @@ funktioniert immer.
* **Runden ist keine Lösung:** Aufrunden ist fast immer unzulässig (98–100 % der Fälle), Abrunden verliert im Mittel 10–26 %, im Extremfall alles.
* **Branch-and-Bound** löst LP-Relaxationen, verzweigt an gebrochenen Variablen und kappt Äste anhand von Schranken. Der Preis: NP-Schwere.
* **Fünf Modellierungsmuster** decken die meisten Praxisfälle ab: Fixkosten, Entweder-Oder, Wenn-Dann, Kardinalität, semikontinuierlich.
-* **$M$ so klein wie möglich** — nicht nur wegen der Laufzeit. Ein übergroßes $M$ kann Binärvariablen bei $10^{-8}$ hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (*Trickle Flow*`\index{Trickle Flow}`{=latex}).
+* **$M$ so klein wie möglich** — nicht nur wegen der Laufzeit. Ein übergroßes $M$ kann Binärvariablen bei $10^{-8}$ hängen lassen, wo sie dem Solver als 0 gelten: Anlagen produzieren, ohne dass ihre Fixkosten je verbucht werden (*[Trickle Flow](#gloss:trickle-flow){.glossar-link}*`\index{Trickle Flow}`{=latex}).
* **`OPTIMAL` ist nicht der Normalfall.** Werten Sie jeden Status aus und lesen Sie den **MIP-Gap** — er ist eine Garantie („höchstens 2,3 % vom Optimum“), keine Schätzung. Der Beweis der Optimalität kostet meist ein Vielfaches dessen, was das Finden der optimalen Lösung kostet.
* **Warm-Starts können nichts verfälschen, aber auch nichts versprechen.** Ob ein Startwert hilft, hängt davon ab, ob er besser ist als das, was der Solver in derselben Zeit von allein findet — im Buch gemessen: einmal Faktor 2,2, einmal gar nichts.
* **Prüfen Sie jede modellierte Regel** nach dem Lösen mit einer `assert`-Zeile — und rechnen Sie den Zielwert aus der Lösung nach, statt ihm zu glauben.
@@ -7613,7 +7613,7 @@ funktioniert immer.
>
> **Voraussetzungen:** [Kapitel 6](#kap-milp) (Binärvariablen, logische Bedingungen).
>
-> **Danach können Sie:** Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten`\index{Strafkosten}`{=latex} steuern — und alle fünf Solver-Antworten auseinanderhalten, statt nur auf `OPTIMAL` zu prüfen.
+> **Danach können Sie:** Zuweisungs-, [Scheduling](#gloss:scheduling){.glossar-link}- und Reihenfolgeprobleme mit [CP-SAT](#gloss:cp-sat){.glossar-link} modellieren, globale Constraints einsetzen, weiche Ziele über [Strafkosten](#gloss:strafkosten){.glossar-link}`\index{Strafkosten}`{=latex} steuern — und alle fünf [Solver](#gloss:solver){.glossar-link}-Antworten auseinanderhalten, statt nur auf `OPTIMAL` zu prüfen.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
>
@@ -7668,7 +7668,7 @@ funktioniert immer.
> Fr -> Abel
> ```
-**Elf Zeilen Modell, und der Plan steht.** Beachten Sie besonders die Zeile `m.AddExactlyOne(...)`. Sie sagt wörtlich, was gemeint ist: *genau eine* Person je Tag. In MILP hätte man dafür eine Gleichung $\sum_p x_{p,d} = 1$ aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in CP-SAT bleibt er eine Zeile.
+**Elf Zeilen Modell, und der Plan steht.** Beachten Sie besonders die Zeile `m.AddExactlyOne(...)`. Sie sagt wörtlich, was gemeint ist: *genau eine* Person je Tag. In [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} hätte man dafür eine Gleichung $\sum_p x_{p,d} = 1$ aufgestellt — machbar, aber schon eine Übersetzungsleistung. Bei Regeln wie „diese beiden Dienste nicht direkt hintereinander“ oder „höchstens eine Nachtschicht pro Woche“ wächst dieser Übersetzungsaufwand rasch; in [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} bleibt er eine Zeile.
**Und jetzt sehen Sie sich das Ergebnis genau an.** *Cor bekommt keinen einzigen Dienst.* Dane arbeitet Montag **und** Dienstag hintereinander. Ist das ein Fehler?
@@ -7683,29 +7683,29 @@ Nein — und das ist die wichtigste Lektion dieses Kapitels. Der Plan erfüllt *
Nach diesem Kapitel können Sie …
1. … erklären, wodurch sich Constraint Programming von MILP unterscheidet.
-2. … die Wirkung von **Constraint Propagation**`\index{Constraint Propagation}`{=latex} an einem Beispiel nachvollziehen.
+2. … die Wirkung von **[Constraint Propagation](#gloss:constraint-propagation){.glossar-link}**`\index{Constraint Propagation}`{=latex} an einem Beispiel nachvollziehen.
3. … die wichtigsten globalen Constraints einsetzen: `AddExactlyOne`, `AddAllDifferent`, `AddNoOverlap`, `AddCumulative`.
4. … mit **Intervallvariablen**`\index{Intervallvariable}`{=latex} ein Maschinenbelegungsproblem modellieren.
5. … harte Regeln und weiche Ziele in einem Modell kombinieren und gewichten.
6. … die fünf CP-SAT-Statusfälle unterscheiden — insbesondere `INFEASIBLE` (es gibt keine Lösung) von `UNKNOWN` (es wurde keine gefunden) und `MODEL_INVALID` (Ihr Code ist fehlerhaft).
7. … begründen, warum `num_workers = 1` für einen reproduzierbaren Lauf nötig ist und ein fester `random_seed` allein nicht genügt — und was ein Test deshalb prüfen darf.
-8. … begründen, warum ein Strafgewicht ein **Wechselkurs** ist, und welche Regeln deshalb niemals in die Zielfunktion`\index{Zielfunktion}`{=latex} gehören.
+8. … begründen, warum ein Strafgewicht ein **Wechselkurs** ist, und welche Regeln deshalb niemals in die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} gehören.
---
## 7.3 Ein anderes Denkmodell {#sec:cpsat-ein-anderes-denkmodell}
-Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt **Constraint Programming (CP)**`\index{Constraint Programming (CP)}`{=latex} aus der künstlichen Intelligenz:
+Während [LP](#gloss:lineare-programmierung-lp){.glossar-link} und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt **Constraint Programming (CP)**`\index{Constraint Programming (CP)}`{=latex} aus der künstlichen Intelligenz:
| | MILP | Constraint Programming |
| --- | --- | --- |
| Variablen | reell oder ganzzahlig, mit Schranken | diskrete **Wertebereiche** (*domains*) |
| Bedingungen | lineare Ungleichungen | beliebige logische Relationen |
-| Suchprinzip | Relaxation`\index{Relaxation}`{=latex} + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
+| Suchprinzip | [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
| Stärke | ökonomische Modelle, Kosten, Kapazitäten | Zuordnung, Reihenfolge, Zeitfenster, Regeln |
| Schwäche | viele logische Regeln → viele Big-M | reine Kostenoptimierung über Kontinuum |
-Der **CP-SAT-Solver**`\index{CP-SAT-Solver}`{=latex} von Google OR-Tools verbindet Constraint Programming mit modernen **SAT-Techniken**`\index{SAT (Boolean Satisfiability)}`{=latex} (*Boolean Satisfiability*, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere **CDCL**`\index{CDCL}`{=latex} (*Conflict-Driven Clause Learning*, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und Job-Shop-Scheduling`\index{Job-Shop-Scheduling}`{=latex}.
+Der **[CP-SAT-Solver](#gloss:cp-sat-solver){.glossar-link}**`\index{CP-SAT-Solver}`{=latex} von Google [OR-Tools](#gloss:or-tools){.glossar-link} verbindet Constraint Programming mit modernen **SAT-Techniken**`\index{SAT (Boolean Satisfiability)}`{=latex} (*Boolean Satisfiability*, Erfüllbarkeitsproblem der Aussagenlogik), insbesondere **[CDCL](#gloss:cdcl){.glossar-link}**`\index{CDCL}`{=latex} (*Conflict-Driven Clause Learning*, konfliktgetriebenes Klausellernen). Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung, Projektterminierung und [Job-Shop-Scheduling](#gloss:job-shop-scheduling){.glossar-link}`\index{Job-Shop-Scheduling}`{=latex}.
### Was Propagation leistet
@@ -7823,12 +7823,12 @@ CP-SAT stellt mächtige Makros`\index{Globale Constraints}`{=latex} bereit, die
| `NewIntervalVar(start, size, end, name)` | Zeitintervall mit Start, Dauer, Ende | Aufträge, Operationen, Belegungen |
| `AddNoOverlap(intervals)` | Intervalle überschneiden sich **nicht** | Eine Maschine, ein Raum, eine Person |
| `AddCumulative(intervals, bedarfe, kapazitaet)` | Zu **keinem Zeitpunkt** übersteigt der Parallelverbrauch die Kapazität | Personalstärke, Stromlast, Kranstunden |
-| `AddCircuit(arcs)` | Die gewählten Kanten bilden **einen** Rundweg | TSP-artige Reihenfolgeprobleme |
+| `AddCircuit(arcs)` | Die gewählten Kanten bilden **einen** Rundweg | [TSP](#gloss:tsp-traveling-salesperson-problem){.glossar-link}-artige Reihenfolgeprobleme |
| `.OnlyEnforceIf(bool)` | Bedingung gilt **nur wenn** | Reifizierung: Regel an-/abschaltbar machen |
> **🔤 Formel-Übersetzer: was ein globales Constraint zusichert**
>
-> Globale Constraints tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
+> [Globale Constraints](#gloss:globale-constraints){.glossar-link} tragen englische Methodennamen, meinen aber alltägliche Regeln. Links die mathematische Zusicherung, rechts der Satz, den Sie im Planungsgespräch sagen würden:
>
> | Mathematik | Alltagssprache |
> | --- | --- |
@@ -7842,7 +7842,7 @@ CP-SAT stellt mächtige Makros`\index{Globale Constraints}`{=latex} bereit, die
>
> **Der Unterschied zu MILP in einem Satz:** Dort formuliert man *Ungleichungen, aus denen die Regel folgt*; hier schreibt man **die Regel selbst** hin.
-> **💡 Warum globale Constraints so viel bringen** `AddAllDifferent` über 9 Variablen ersetzt $\binom{9}{2} = 36$ paarweise Ungleichheitsbedingungen — und propagiert **stärker** als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten Graphen). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
+> **💡 Warum globale Constraints so viel bringen** `AddAllDifferent` über 9 Variablen ersetzt $\binom{9}{2} = 36$ paarweise Ungleichheitsbedingungen — und propagiert **stärker** als die 36 Einzelbedingungen zusammen, weil der Propagator die Struktur als Ganzes betrachtet (er nutzt Matching-Theorie auf bipartiten [Graphen](#gloss:graphen){.glossar-link}). Ein MILP-Modell derselben Regel bräuchte Big-M-Konstruktionen und würde deutlich langsamer laufen.
---
@@ -8151,13 +8151,13 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
>
> **2. Fairness über Spannweite (W3).** `AddMaxEquality` und `AddMinEquality` sind CP-SAT-Konstrukte, die in MILP mehrere Big-M-Ungleichungen bräuchten. Die Spannweite $\max - \min$ zu minimieren ist ein Standardrezept für „gleichmäßig verteilen“.
>
-> **3. Kostenzerlegung`\index{Kostenzerlegung}`{=latex}.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: *„Warum bekomme **ich** die Stunde?“* Wer darauf keine Antwort hat, dessen System wird nicht benutzt ([Kapitel 22](#kap-praxisfallen), *Explainable OR*`\index{Explainable OR}`{=latex}).
+> **3. [Kostenzerlegung](#gloss:kostenzerlegung){.glossar-link}`\index{Kostenzerlegung}`{=latex}.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In der Praxis ist die erste Frage jedes Anwenders: *„Warum bekomme **ich** die Stunde?“* Wer darauf keine Antwort hat, dessen System wird nicht benutzt ([Kapitel 22](#kap-praxisfallen), *[Explainable OR](#gloss:explainable-or){.glossar-link}*`\index{Explainable OR}`{=latex}).
>
> **4. Statusauswertung ohne CP-SAT-Vokabular.** Nach `Solve()` steht nur eine einzige Zeile mit einer CP-SAT-Konstante darin:
> ```python
> status = status_von_cpsat(rohstatus)
> ```
-> Danach heißen die Fälle `status.modellfehler` (die Regeln widersprechen sich — das Modell muss geändert werden) und `status.brauchbar` (es liegt etwas Ausführbares vor). Das ist **dieselbe** Fallunterscheidung wie bei HiGHS`\index{HiGHS}`{=latex} in [Kapitel 6](#kap-milp) und bei GLOP in [Kapitel 5](#kap-lp); nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen `cp_model.INFEASIBLE` schreibt, kann ihn bei einem Solverwechsel wegwerfen — [Abschnitt 22.7](#sec:praxisfallen-solverwechsel) führt genau diesen Wechsel vor.
+> Danach heißen die Fälle `status.modellfehler` (die Regeln widersprechen sich — das Modell muss geändert werden) und `status.brauchbar` (es liegt etwas Ausführbares vor). Das ist **dieselbe** Fallunterscheidung wie bei [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex} in [Kapitel 6](#kap-milp) und bei [GLOP](#gloss:glop){.glossar-link} in [Kapitel 5](#kap-lp); nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den Auswertungscode direkt gegen `cp_model.INFEASIBLE` schreibt, kann ihn bei einem Solverwechsel wegwerfen — [Abschnitt 22.7](#sec:praxisfallen-solverwechsel) führt genau diesen Wechsel vor.
> **⚠️ Typische Fehler**
>
@@ -8171,7 +8171,7 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
Die Königsdisziplin von CP-SAT sind Zeitplanungsprobleme. Intervallvariablen sind dabei das zentrale Werkzeug.
-**Problem.**`\index{Job-Shop-Scheduling}`{=latex} Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (*Makespan*`\index{Makespan}`{=latex}).
+**Problem.**`\index{Job-Shop-Scheduling}`{=latex} Drei Aufträge, je drei Arbeitsgänge auf drei Maschinen. Jeder Auftrag muss seine Gänge in fester Reihenfolge durchlaufen; jede Maschine kann nur einen Gang gleichzeitig bearbeiten. Gesucht: der Plan mit der kürzesten Gesamtdauer (*[Makespan](#gloss:makespan){.glossar-link}*`\index{Makespan}`{=latex}).
```python
#!/usr/bin/env python3
@@ -8609,7 +8609,7 @@ random_seed = 1 in JEDEM Lauf, 4 Wiederholungen je Arbeiterzahl.
### Was die Messung zeigt
-**Erstens: Die Beschleunigung ist überlinear.** Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern **gut zwölfmal** schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt **nicht** achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern **verschiedene Strategien nebeneinander** — Suche mit LP-Relaxation`\index{LP-Relaxation}`{=latex}, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
+**Erstens: Die Beschleunigung ist überlinear.** Acht Arbeiter sind im abgedruckten Lauf nicht achtmal, sondern **gut zwölfmal** schneller als einer. Das sieht nach Messfehler aus, ist aber die korrekte Beschreibung dessen, was CP-SAT tut: Es lässt **nicht** achtmal dieselbe Suche auf verschiedenen Kernen laufen, sondern **verschiedene Strategien nebeneinander** — Suche mit [LP-Relaxation](#gloss:lp-relaxation){.glossar-link}`\index{LP-Relaxation}`{=latex}, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“, sondern ein anderes Verfahren, das zufällig auch rechnet.
Daraus folgt zweierlei. Ein Speedup über der Kernzahl ist bei CP-SAT normal und kein Grund zum Misstrauen. Und der Zugewinn flacht ab: Zwischen vier und acht Arbeitern lag in unseren Wiederholungen mal das eine, mal das andere vorn — ab einer gewissen Zahl kosten Abstimmung und Speicherbandbreite mehr, als ein weiterer Suchstrang einbringt. Auch hier gilt: messen, nicht schätzen.
@@ -8617,7 +8617,7 @@ Daraus folgt zweierlei. Ein Speedup über der Kernzahl ist bei CP-SAT normal und
Es braucht also keine acht Arbeiter, um die Reproduzierbarkeit zu verlieren. Zwei genügen. Der Grund ist einfach, sobald man ihn ausspricht: Der Seed legt fest, wie ein **einzelner** Suchstrang würfelt. Er legt nicht fest, **welcher von mehreren zuerst fertig wird** — das entscheidet die Uhr, und die ist bei jedem Lauf anders.
-> **⚠️ `random_seed` allein macht nichts reproduzierbar** Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen **parallelen** Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter`\index{Parameter (Modell)}`{=latex} festnageln. Reproduzierbar wird der Lauf erst mit `num_workers = 1` **und** Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
+> **⚠️ `random_seed` allein macht nichts reproduzierbar** Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator stimmt das. Für einen **parallelen** Solver nicht — dort ist die zweite Zufallsquelle die Ausführungsreihenfolge, und die lässt sich nicht mit einem [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} festnageln. Reproduzierbar wird der Lauf erst mit `num_workers = 1` **und** Seed. Wer nur eines von beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
### Was daraus für Tests folgt
@@ -8962,7 +8962,7 @@ genau die Information, die zur Fehlersuche noetig waere.
**Aufgabe 7.3 ⭐⭐ — Regeln ergänzen.** Erweitern Sie das Vertretungssystem um: (a) Frau Albrecht ist ab Slot 3 nicht mehr verfügbar. (b) Herr Bauer und Frau Koch sollen nicht beide am selben Tag eingesetzt werden. (c) Wer Slot 1 übernimmt, soll bevorzugt auch Slot 2 bekommen (Belohnung statt Strafe).
-**Aufgabe 7.4 ⭐⭐ — Infeasibility`\index{Infeasibility}`{=latex} erzeugen und diagnostizieren.** Setzen Sie `MAX_VERTRETUNGEN = 0`. Was meldet das Programm? Bauen Sie anschließend eine **Schlupfvariable**`\index{Schlupfvariable}`{=latex} je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
+**Aufgabe 7.4 ⭐⭐ — [Infeasibility](#gloss:infeasibility){.glossar-link}`\index{Infeasibility}`{=latex} erzeugen und diagnostizieren.** Setzen Sie `MAX_VERTRETUNGEN = 0`. Was meldet das Programm? Bauen Sie anschließend eine **Schlupfvariable**`\index{Schlupfvariable}`{=latex} je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er ausfallen und warum?
**Aufgabe 7.5 ⭐⭐ — Sudoku.** Lösen Sie ein 9×9-Sudoku mit CP-SAT. Sie brauchen nur `NewIntVar(1, 9, ...)` und `AddAllDifferent` für Zeilen, Spalten und die neun 3×3-Blöcke. Wie viele Zeilen Modellcode benötigen Sie?
@@ -9177,7 +9177,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
>
> * **Gewichte nach Bauchgefühl setzen.** Rechnen Sie stattdessen die Tabelle wie oben und lassen Sie den Auftraggeber die Zeile aussuchen. Das dauert eine Stunde und ersetzt drei Abstimmungsrunden.
> * **Eine unverhandelbare Regel weich modellieren.** Alles, was einen Preis hat, wird irgendwann gekauft. Gesetzliche Ruhezeiten, Qualifikationsanforderungen und Betriebsvereinbarungen gehören unter die **harten** Nebenbedingungen — auch wenn das Modell dann `INFEASIBLE` melden kann. Dieses `INFEASIBLE` ist die ehrliche Antwort: Der Plan ist mit dem vorhandenen Personal nicht zulässig zu machen.
-> * **Gewichte in verschiedenen Einheiten mischen.** Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren Konditionszahl`\index{Konditionszahl}`{=latex} obendrein leidet ([Abschnitt 2.7](#sec:fundament-kondition)).
+> * **Gewichte in verschiedenen Einheiten mischen.** Euro neben „Unzufriedenheitspunkten“ neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren [Konditionszahl](#gloss:konditionszahl){.glossar-link}`\index{Konditionszahl}`{=latex} obendrein leidet ([Abschnitt 2.7](#sec:fundament-kondition)).
> * **Nur ein Gewicht ausprobieren.** Ein einziger Lauf sagt nichts darüber, ob Sie nahe an einem Kipppunkt stehen. Zwischen den Gewichten 5 und 10 ändert sich der Plan oben erheblich.
---
@@ -9213,7 +9213,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
## 7.13 Zusammenfassung {#sec:cpsat-zusammenfassung}
* **CP denkt in Wertebereichen und Regeln**, nicht in Ungleichungen und Schranken.
-* **Propagation** streicht unmögliche Werte, bevor gesucht wird; **Conflict Learning**`\index{CDCL!Conflict Learning}`{=latex} verhindert, dieselbe Sackgasse zweimal zu betreten.
+* **Propagation** streicht unmögliche Werte, bevor gesucht wird; **[Conflict Learning](#gloss:conflict-learning){.glossar-link}**`\index{CDCL!Conflict Learning}`{=latex} verhindert, dieselbe Sackgasse zweimal zu betreten.
* **Globale Constraints** wie `AddAllDifferent``\index{Globale Constraints!AddAllDifferent}`{=latex}, `AddNoOverlap``\index{Globale Constraints!AddNoOverlap}`{=latex} und `AddCumulative``\index{Globale Constraints!AddCumulative}`{=latex} ersetzen ganze Blöcke von Big-M-Ungleichungen und propagieren stärker.
* **Intervallvariablen** sind das Werkzeug für Zeitplanung; `AddNoOverlap` erledigt in einer Zeile, was in MILP dutzende Disjunktionen kostet.
* **Weiche Ziele mit Strafkosten** machen Modelle robust gegen `INFEASIBLE` — und die **Kostenzerlegung** macht sie erklärbar.
@@ -9289,7 +9289,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
> Gesamtfahrzeit: 118 min
> ```
-**Eine Zeile Code, und das Problem ist beweisbar optimal gelöst** — `linear_sum_assignment` ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden Solver auskommt.
+**Eine Zeile Code, und das Problem ist beweisbar optimal gelöst** — `linear_sum_assignment` ist die Ungarische Methode, ein Spezialalgorithmus, der ohne jeden [Solver](#gloss:solver){.glossar-link} auskommt.
Interessanter ist aber, was der Algorithmus **nicht** tut. Der kleinste Wert der ganzen Tabelle ist die 18 bei *Cakir → Nord*. Die naheliegende Vorgehensweise — „nimm immer das günstigste noch freie Paar“ — beginnt also genau dort:
@@ -9302,7 +9302,7 @@ Die optimale Lösung schickt **Bauer** nach Nord, obwohl er dort drei Minuten l
> **🎯 Merksatz** Der beste erste Zug ist selten Teil der besten Gesamtlösung. Genau deshalb gibt es Operations Research: Optimierung heißt, Entscheidungen **gemeinsam** zu treffen statt nacheinander. Bei vier Monteuren kostet die gierige Regel 8 %; bei vierzig kostet sie regelmäßig ein Vielfaches.
-**Warum funktioniert das?** Weil das Zuordnungsproblem`\index{Zuordnungsproblem (1:1)}`{=latex} eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist **total unimodular**. Das bedeutet — wir kommen in [Abschnitt 8.4](#sec:graphen-bipartites-matching-das-zuordnungsproblem) darauf zurück —, dass die LP-Relaxation`\index{LP-Relaxation}`{=latex} von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder Branch-and-Bound`\index{Branch-and-Bound}`{=latex} noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
+**Warum funktioniert das?** Weil das [Zuordnungsproblem](#gloss:zuordnungsproblem){.glossar-link}`\index{Zuordnungsproblem (1:1)}`{=latex} eine besondere Struktur hat: Seine Nebenbedingungsmatrix ist **total unimodular**. Das bedeutet — wir kommen in [Abschnitt 8.4](#sec:graphen-bipartites-matching-das-zuordnungsproblem) darauf zurück —, dass die [LP-Relaxation](#gloss:lp-relaxation){.glossar-link}`\index{LP-Relaxation}`{=latex} von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}`\index{Branch-and-Bound}`{=latex} noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so angenehm lösbar, und damit beginnt das Kapitel.
---
@@ -9313,8 +9313,8 @@ Nach diesem Kapitel können Sie …
1. … ein Transportproblem als Graph mit Quellen, Senken und Kapazitäten modellieren.
2. … den **Flusserhaltungssatz**`\index{Flusserhaltung}`{=latex} aufstellen und seine Bedeutung erklären.
3. … begründen, warum Zuordnungsprobleme **ohne** Ganzzahligkeitsbedingung ganzzahlig lösbar sind (totale Unimodularität`\index{Totale Unimodularität}`{=latex}).
-4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools`\index{OR-Tools}`{=latex} lösen.
-5. … einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
+4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} lösen.
+5. … einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} überlegen ist.
6. … begründen, warum eine gierige Zuordnung systematisch schlechter ist als eine gemeinsame Optimierung.
7. … einen Tourenplan gegen die Wirklichkeit prüfen — unabhängig von den Bausteinen, aus denen das Modell gebaut wurde.
@@ -9322,7 +9322,7 @@ Nach diesem Kapitel können Sie …
## 8.3 Graphen als Modellsprache {#sec:graphen-graphen-als-modellsprache}
-Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern **Graphen** $G = (V, E)$:
+Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern **[Graphen](#gloss:graphen){.glossar-link}** $G = (V, E)$:
* $V$ — die **Knoten** (*vertices*): Server, Depots, Kunden, Konten, Lager.
* $E$ — die gerichteten **Kanten** (*edges*): Datenleitungen, Straßen, Überweisungswege.
@@ -9332,7 +9332,7 @@ Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine fl
**Minimum-Cost Network Flow Problem**`\index{Min-Cost-Flow}`{=latex} — deutsch: *kostenminimales Flussproblem*. Es ist das mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverteilung.

+Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 € bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die Dualwerte der [Flusserhaltung](#gloss:flusserhaltung){.glossar-link}`\index{Flusserhaltung}`{=latex}. Erzeugt von `bilder_04/erzeuge_min_cost_flow.py`.](bilder_04/kap_graphen_min_cost_flow.svg)
Sei $x_{ij} \ge 0$ der Fluss über Kante $(i,j)$, $c_{ij}$ die Kosten je Einheit und $u_{ij}$ die Kapazität:
@@ -9513,7 +9513,7 @@ $$
> **Satz.** Die Extrempunkte der Menge`\index{Konvexität!Menge}`{=latex} aller doppelt-stochastischen Matrizen (alle Zeilensummen $= 1$, alle Spaltensummen $= 1$, $x_{ij} \ge 0$) sind **genau** die Permutationsmatrizen (alle $x_{ij} \in \{0,1\}$).
-**Warum das praktisch enorm wichtig ist:**`\index{Satz von Birkhoff und von Neumann}`{=latex} Nach dem Fundamentalsatz aus [Kapitel 2](#kap-fundament) liegt das LP-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
+**Warum das praktisch enorm wichtig ist:**`\index{Satz von Birkhoff und von Neumann}`{=latex} Nach dem Fundamentalsatz aus [Kapitel 2](#kap-fundament) liegt das [LP](#gloss:lineare-programmierung-lp){.glossar-link}-Optimum in einer Ecke. Die Ecken sind hier laut Satz automatisch 0/1-wertig. Also gilt:
> **🎯 Merksatz** Beim Zuordnungsproblem müssen Sie die Ganzzahligkeit **nicht** fordern — ein gewöhnlicher LP-Solver liefert von selbst eine 0/1-Lösung. Sie sparen sich damit die NP-Schwere von Branch-and-Bound vollständig.
@@ -9529,7 +9529,7 @@ Der Grund dahinter heißt **totale Unimodularität**: Die Nebenbedingungsmatrix
>
> **Die praktische Folge in einem Satz:** *Bei Zuordnungs- und Flussproblemen dürfen Sie die Ganzzahligkeit weglassen und trotzdem ganzzahlige Lösungen erwarten — Sie sparen sich die NP-Schwere von Branch-and-Bound vollständig.*
>
-> **Und die Warnung dazu:** Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine Mindestabnahmemenge`\index{Mindestabnahmemenge}`{=latex} — zerstört die totale Unimodularität. Dann liefert die Relaxation`\index{Relaxation}`{=latex} wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
+> **Und die Warnung dazu:** Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“, eine Fixkostenkopplung, eine [Mindestabnahmemenge](#gloss:mindestabnahmemenge){.glossar-link}`\index{Mindestabnahmemenge}`{=latex} — zerstört die totale Unimodularität. Dann liefert die [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
```python
#!/usr/bin/env python3
@@ -9651,7 +9651,7 @@ if __name__ == "__main__":
## 8.5 Das Vehicle Routing Problem mit Zeitfenstern {#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
-Das **Traveling Salesperson Problem (TSP)**`\index{TSP (Traveling Salesperson Problem)}`{=latex}, deutsch *Problem des Handlungsreisenden*, fragt nach der kürzesten Rundreise durch $N$ Städte. Das **Capacitated Vehicle Routing Problem with Time Windows (CVRPTW)**`\index{CVRPTW}`{=latex} erweitert es auf eine **Flotte** mit Kapazitätsgrenzen und Kundenzeitfenstern $[e_i, l_i]$.
+Das **Traveling Salesperson Problem ([TSP](#gloss:tsp-traveling-salesperson-problem){.glossar-link})**`\index{TSP (Traveling Salesperson Problem)}`{=latex}, deutsch *Problem des Handlungsreisenden*, fragt nach der kürzesten Rundreise durch $N$ Städte. Das **Capacitated Vehicle Routing Problem with Time Windows ([CVRPTW](#gloss:cvrptw){.glossar-link})**`\index{CVRPTW}`{=latex} erweitert es auf eine **Flotte** mit Kapazitätsgrenzen und Kundenzeitfenstern $[e_i, l_i]$.

@@ -9664,7 +9664,7 @@ Die Touren sind aus demselben Grund **Luftlinien und keine Straßenrouten**: Das
### Kurzzyklen verhindern
-Ein naives Modell erlaubt **Subtouren**`\index{Subtour}`{=latex}: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die **MTZ-Formulierung**`\index{MTZ-Formulierung}`{=latex} nach Miller, Tucker und Zemlin. Man führt Rangvariablen $u_i$ ein (die Position des Knotens in der Tour):
+Ein naives Modell erlaubt **Subtouren**`\index{Subtour}`{=latex}: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die **[MTZ-Formulierung](#gloss:mtz-formulierung){.glossar-link}**`\index{MTZ-Formulierung}`{=latex} nach Miller, Tucker und Zemlin. Man führt Rangvariablen $u_i$ ein (die Position des Knotens in der Tour):

@@ -9679,7 +9679,7 @@ $$
>
> Wird die Kante nicht benutzt ($x_{ij} = 0$), reduziert sich die Ungleichung auf $u_i - u_j \le C - d_j$, was durch hinreichend großes $C$ immer erfüllt ist — das Big-M-Muster aus [Kapitel 6](#kap-milp).
-> **💡 In der Praxis: nicht selbst modellieren** Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte Subtour`\index{Subtour}`{=latex}-Eliminationsschnitte oder, wie OR-Tools, **spezialisierte Metaheuristiken**. Nutzen Sie für Routing die **Routing-Bibliothek**`\index{Routing-Bibliothek}`{=latex}, nicht ein selbstgebautes MILP.
+> **💡 In der Praxis: nicht selbst modellieren** Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle Solver verwenden stattdessen dynamisch erzeugte [Subtour](#gloss:subtour){.glossar-link}`\index{Subtour}`{=latex}-Eliminationsschnitte oder, wie OR-Tools, **spezialisierte Metaheuristiken**. Nutzen Sie für Routing die **[Routing-Bibliothek](#gloss:routing-bibliothek){.glossar-link}**`\index{Routing-Bibliothek}`{=latex}, nicht ein selbstgebautes MILP.
### Praxisbeispiel: Flotten-Routing
@@ -9851,7 +9851,7 @@ if __name__ == "__main__":
loese_cvrptw()
```
-> **⚠️ Typische Fehler beim VRP**
+> **⚠️ Typische Fehler beim [VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link}**
>
> * **`Min(CumulVar)` mit „Ankunftszeit“ verwechseln.** Der Solver liefert ein *Intervall* möglicher Zeiten. `Min()` ist die früheste, `Max()` die späteste zulässige Zeit — die tatsächliche Fahrt kann irgendwo dazwischen starten.
> * **Servicezeit im Depot mitzählen.** Beim Start am Depot fällt keine Servicezeit an, sonst verschiebt sich der ganze Plan.
@@ -10123,7 +10123,7 @@ plausibel aussieht.
>
> * **Eine Dimension vergessen.** Ladung, Lenkzeit, Kühlkette, Gewicht *und* Volumen — jede Größe, die begrenzt ist, braucht ihre eigene Dimension. Zählen Sie sie vor dem Modellieren auf einem Blatt Papier auf.
> * **Die Prüfung aus denselben Bausteinen bauen wie das Modell.** Wer die Ladung mit `loesung.Value(ladungs_dimension.CumulVar(...))` prüft, fragt das Modell, ob es sich an sich selbst hält. Rechnen Sie stattdessen aus der **ausgegebenen Tour** neu nach.
-> * **Zwei Läufe nur an der Zielfunktion`\index{Zielfunktion}`{=latex} vergleichen.** 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
+> * **Zwei Läufe nur an der [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} vergleichen.** 326 gegen 572 km sagt nichts, solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
> * **Unbenutzte Fahrzeuge übersehen.** Drei Fahrzeuge, die im Depot stehen, während eines alles fährt, sind fast immer ein Zeichen für eine fehlende Beschränkung.
---
@@ -10158,14 +10158,14 @@ plausibel aussieht.
* **Graphen** sind die natürliche Sprache für Transport-, Zuordnungs- und Routenprobleme.
* **Flusserhaltung** ist die Kirchhoff-Regel des Operations Research: Was hineingeht, kommt heraus — abzüglich des Knotensaldos.
-* **Totale Unimodularität**`\index{Totale Unimodularität}`{=latex} macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier `integrality` setzt, verschenkt Laufzeit ohne Gegenwert.
+* **[Totale Unimodularität](#gloss:totale-unimodularitaet){.glossar-link}**`\index{Totale Unimodularität}`{=latex} macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer hier `integrality` setzt, verschenkt Laufzeit ohne Gegenwert.
* **Spezialisierte Algorithmen schlagen allgemeine Solver** deutlich: Der Ungarische Algorithmus`\index{Ungarischer Algorithmus}`{=latex} löst in $O(n^3)$, wofür ein MILP-Solver Branch-and-Bound bräuchte.
* **Für Routing gilt: Nutzen Sie die Routing-Bibliothek.**`\index{Routing-Bibliothek}`{=latex} Metaheuristiken liefern in Sekunden sehr gute Touren; exakte Optimalität ist bei realistischen Größen unrealistisch und praktisch entbehrlich.
* **Die Routing-Bibliothek kennt nur Dimensionen.** Ladung, Lenkzeit, Kühlkette, Gewicht — jede begrenzte Größe braucht ihre eigene. Was nicht als Dimension angelegt ist, wird nicht begrenzt, und der Solver meldet stolz eine kürzere Strecke.
* **Totale Unimodularität ist zerbrechlich.** Eine einzige zusätzliche Bedingung, die nicht ins Schema passt — Kardinalität, Fixkosten, Mindestmenge —, zerstört sie. Prüfen Sie das, bevor Sie sich auf die Struktur verlassen.
* **Der beste erste Zug ist selten Teil der besten Gesamtlösung.** Gierige Regeln kosten schon bei vier Zuordnungen 8 %.
-**Ausblick.** [Teil III](#teil-nichtlinear) verlässt die lineare Welt. [Kapitel 11](#kap-qp-nlp) führt quadratische Zielfunktionen und die KKT-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
+**Ausblick.** [Teil III](#teil-nichtlinear) verlässt die lineare Welt. [Kapitel 11](#kap-qp-nlp) führt quadratische Zielfunktionen und die [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}-Bedingungen ein — das mathematische Fundament der Portfoliooptimierung.
---
@@ -10174,11 +10174,11 @@ plausibel aussieht.
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der Solver kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
+> **Worum geht es?** Um den Fall, für den die bisherigen Kapitel keine Antwort haben: Das Problem ist zu groß, der [Solver](#gloss:solver){.glossar-link} kommt im Zeitlimit nicht zu einem brauchbaren Ergebnis — und eine Entscheidung muss trotzdem heute fallen.
>
-> **Voraussetzungen:** [Kapitel 6](#kap-milp) und [Kapitel 7](#kap-cpsat), insbesondere [Abschnitt 6.8](#sec:milp-gap) (MIP-Gap`\index{MIP-Gap}`{=latex} und Schranke).
+> **Voraussetzungen:** [Kapitel 6](#kap-milp) und [Kapitel 7](#kap-cpsat), insbesondere [Abschnitt 6.8](#sec:milp-gap) ([MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex} und Schranke).
>
-> **Danach können Sie:** eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing`\index{Simulated Annealing}`{=latex} ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
+> **Danach können Sie:** eine lokale Suche mit ihrer entscheidenden Zutat — der Kostenänderung in O(1) — selbst schreiben, die Temperatur eines [Simulated Annealing](#gloss:simulated-annealing){.glossar-link}`\index{Simulated Annealing}`{=latex} ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
>
> **Zeitbedarf:** ca. 5 Stunden.
>
@@ -10250,7 +10250,7 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum eine lokale Suche ohne **Kostenänderung in O(1)** praktisch wertlos ist.
2. … Simulated Annealing implementieren und seine Starttemperatur **an der Zuggröße** kalibrieren statt am Zielfunktionswert.
-3. … den Beitrag der Metaheuristik`\index{Metaheuristik}`{=latex} gegen die einfachere Alternative abgrenzen — reines Bergsteigen — statt ihn ihr gutzuschreiben.
+3. … den Beitrag der [Metaheuristik](#gloss:metaheuristik){.glossar-link}`\index{Metaheuristik}`{=latex} gegen die einfachere Alternative abgrenzen — reines Bergsteigen — statt ihn ihr gutzuschreiben.
4. … den **Umschlagpunkt** bestimmen, ab dem eine Metaheuristik den exakten Solver schlägt, und begründen, warum diese Zahl problemspezifisch ist.
5. … erklären, warum ein exakter Lauf auch dann dazugehört, wenn Sie am Ende die heuristische Lösung einsetzen.
6. … Large Neighborhood Search aufsetzen: zerstören, exakt reparieren, übernehmen.
@@ -10278,7 +10278,7 @@ $$
>
> **Ohne Formel gesagt:** „Sortiere die Aufträge so, dass möglichst wenig geputzt werden muss.“
-Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein **asymmetrisches Rundreiseproblem** ([Kapitel 8](#kap-graphen)) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es NP-schwer`\index{NP-schwer}`{=latex} ist.
+Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein **asymmetrisches Rundreiseproblem** ([Kapitel 8](#kap-graphen)) mit offenem Ende. Das ist eine gute und eine schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen kann. Schlecht, weil dazu gehört, dass es [NP-schwer](#gloss:np-schwer){.glossar-link}`\index{NP-schwer}`{=latex} ist.
> **📖 Definition: Metaheuristik**`\index{Metaheuristik}`{=latex}
>
@@ -10288,7 +10288,7 @@ Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein **asymmetrisches
### Die Instanz
-Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit`\index{Rüstzeit}`{=latex} setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
+Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex} setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und einem Zuschlag für den Wechsel von Dunkel nach Hell.
Eine Feinheit der Instanz ist wichtiger, als sie aussieht: Die **Zahl der Produktfamilien wächst mit der Auftragszahl** (eine Familie je zehn Aufträge). Bei fester Familienzahl würde das Problem mit wachsendem $n$ nämlich *leichter* — der Plan bestünde irgendwann aus ein paar großen Blöcken, und jede Faustregel fände ihn. Beim ersten Entwurf dieses Kapitels ist mir genau das passiert: Die Metaheuristik verbesserte bei 60 Aufträgen um 30 %, bei 200 Aufträgen um 0 %. Nicht weil sie versagte, sondern weil es nichts mehr zu verbessern gab.
@@ -10992,7 +10992,7 @@ if __name__ == "__main__":
print("=" * 88)
```
-**Erwartete Ausgabe** (die CP-SAT`\index{CP-SAT}`{=latex}-Spalten sind hardwareabhängig, siehe Programmkopf):
+**Erwartete Ausgabe** (die [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}-Spalten sind hardwareabhängig, siehe Programmkopf):
```
========================================================================================
@@ -11039,7 +11039,7 @@ Drei Größen, drei völlig verschiedene Empfehlungen:
| Aufträge | Empfehlung |
| ---: | --- |
-| 60 | **Exakt.** CP-SAT beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht. |
+| 60 | **Exakt.** [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} beweist das Optimum in 2 Sekunden. Die Metaheuristik liegt 3,8 % daneben — und wüsste es nicht. |
| 200 | **Exakt.** Kein Beweis mehr, aber der Gap liegt unter 1 %, und der Wert ist rund 11 % besser als der heuristische. |
| 500 | **Heuristisch.** CP-SAT liefert eine Lösung, die 5,2 % **schlechter** ist als die Faustregel eines Meisters. |
@@ -11059,7 +11059,7 @@ Das ist der Unterschied zwischen *besser als vorher* und *gut*. Eine Metaheurist
Die Tabelle oben endet mit einem Widerspruch. Bei 500 Aufträgen scheitert der exakte Solver an der **Größe**; die Metaheuristik scheitert an der **Kleinheit ihrer Züge** — sie verschiebt jeweils einen einzigen Auftrag und kann eine ganze Passage nicht auf einmal umbauen.
-**Large Neighborhood Search**`\index{Large Neighborhood Search}`{=latex} (LNS) setzt beide ein, jeden für das, was er kann:
+**Large Neighborhood Search**`\index{Large Neighborhood Search}`{=latex} ([LNS](#gloss:large-neighborhood-search-lns){.glossar-link}) setzt beide ein, jeden für das, was er kann:
| Schritt | Was passiert |
| --- | --- |
@@ -11469,7 +11469,7 @@ Damit die Landkarte vollständig ist — drei Verfahren, die in dieselbe Familie
| Verfahren | Idee in einem Satz | Wann anschauen |
| --- | --- | --- |
| **Genetische Algorithmen**`\index{Genetischer Algorithmus}`{=latex} | Eine Population von Lösungen wird gekreuzt und mutiert. | Wenn es eine natürliche „Kreuzung“ zweier Lösungen gibt. Für Reihenfolgen ist genau das der schwierige Teil. Bibliothek: `pymoo`. |
-| **Tabu-Suche**`\index{Tabu-Suche}`{=latex} | Bergsteigen, das zuletzt gemachte Züge eine Zeit lang verbietet. | Wenn die Suche zwischen zwei Zuständen pendelt. Oft mit weniger Kalibrierung als Annealing. |
+| **[Tabu-Suche](#gloss:tabu-suche){.glossar-link}**`\index{Tabu-Suche}`{=latex} | Bergsteigen, das zuletzt gemachte Züge eine Zeit lang verbietet. | Wenn die Suche zwischen zwei Zuständen pendelt. Oft mit weniger Kalibrierung als Annealing. |
| **Spaltengenerierung**`\index{Column Generation}`{=latex} | Nicht die Lösung heuristisch machen, sondern das **Modell** zerlegen: Ein Master-Problem fragt bei einem Teilproblem nach neuen, lohnenden Spalten. | Bei Zuschnitt- und Dienstplanproblemen mit sehr vielen gleichartigen Mustern. **Das folgende Kapitel** führt es aus: [Kapitel 10](#kap-dekomposition). |
Die Spaltengenerierung ist die interessanteste davon, weil sie eine **andere Antwort auf dieselbe Frage** gibt: Statt die Lösung zu approximieren, formuliert sie das Modell so um, dass exaktes Lösen wieder möglich wird. Sie hat eine eigene Voraussetzung — das Problem muss sich in ein Master- und ein Teilproblem zerlegen lassen — und ist deshalb kein Ersatz für LNS, sondern eine Alternative für eine engere Problemklasse.
@@ -11535,7 +11535,7 @@ Genau deshalb bekommt sie ein eigenes Kapitel: [Kapitel 10](#kap-dekomposition).
## 9.13 Zusammenfassung {#sec:metaheuristiken-zusammenfassung}
* Eine **Faustregel** ist selten dumm, aber immer kurzsichtig: Sie bewertet einen Schritt danach, was er kostet, nicht danach, was er übrig lässt.
-* **Lokale Suche** steht und fällt mit der Kostenänderung in $O(1)$. Wer den Zug über die volle Zielfunktion`\index{Zielfunktion}`{=latex} bewertet, misst am Ende seine Implementierung statt des Verfahrens.
+* **Lokale Suche** steht und fällt mit der Kostenänderung in $O(1)$. Wer den Zug über die volle [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} bewertet, misst am Ende seine Implementierung statt des Verfahrens.
* **Simulated Annealing** nimmt Verschlechterungen mit $e^{-\Delta/T}$ an. $T$ und $\Delta$ haben dieselbe Einheit — die Temperatur gehört an der **Zuggröße** kalibriert, nicht am Zielfunktionswert. Die verbreitete Regel „20 bis 50 % Annahmequote“ war auf dieser Aufgabe falsch.
* Der **schlechteste Zwischenwert** ist die beste Diagnosezahl eines Annealing-Laufs: Er zeigt eine zu heiße Starttemperatur sofort, ohne dass man das Ergebnis abwarten muss.
* Rechnen Sie **immer** das simple Bergsteigen mit. Hier lieferte es 93 % des Ertrags.
@@ -11550,7 +11550,7 @@ Genau deshalb bekommt sie ein eigenes Kapitel: [Kapitel 10](#kap-dekomposition).
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um die andere Antwort auf die Frage aus [Kapitel 9](#kap-metaheuristiken): Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
+> **Worum geht es?** Um die andere Antwort auf die Frage aus [Kapitel 9](#kap-metaheuristiken): Was tun, wenn der [Solver](#gloss:solver){.glossar-link} an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.
>
> **Voraussetzungen:** [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise) (Dualwerte) und [Kapitel 6](#kap-milp) (`Rucksack.py`). Beides wird gebraucht, aber nicht neu erklärt.
>
@@ -11576,7 +11576,7 @@ Genau deshalb bekommt sie ein eigenes Kapitel: [Kapitel 10](#kap-dekomposition).
> ```python
> import itertools
> import numpy as np
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> ROLLE = 1000 # mm Mutterrolle
> breiten, bedarf = [420, 310, 250], [9, 12, 14]
@@ -11611,7 +11611,7 @@ Genau deshalb bekommt sie ein eigenes Kapitel: [Kapitel 10](#kap-dekomposition).
Das ist kein kosmetischer Unterschied. Das Modell hat **eine Variable je Muster** statt einer je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschwindet ein Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.
-> **🎯 Merksatz** Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion`\index{Zielfunktion}`{=latex}. Es ist die Frage, **worüber** die Variablen laufen.
+> **🎯 Merksatz** Die schwierigste Arbeit an einem Optimierungsmodell ist selten die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}. Es ist die Frage, **worüber** die Variablen laufen.
**Warum funktioniert das?** Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei Breiten und einer 1 000-mm-Rolle sind es 16; bei dreizehn Breiten und 5 600 mm sind es zehntausende, und bei einer echten Papierfabrik mehr, als sich speichern lässt. Das ganze Kapitel handelt davon, wie man mit Mustern rechnet, ohne sie aufzuschreiben.
@@ -11623,7 +11623,7 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum das naheliegende Zuordnungsmodell an **Symmetrie** scheitert.
2. … ein Problem in **Master** und **Pricing-Teilproblem** zerlegen.
-3. … die Schleife schreiben: Master-LP lösen, Dualwerte auslesen, Teilproblem lösen, Spalte hinzufügen.
+3. … die Schleife schreiben: Master-[LP](#gloss:lineare-programmierung-lp){.glossar-link} lösen, Dualwerte auslesen, Teilproblem lösen, Spalte hinzufügen.
4. … das Abbruchkriterium über die **reduzierten Kosten** begründen.
5. … an einer Kennzahl der Instanz abschätzen, ob sich das Verfahren lohnt.
@@ -11637,7 +11637,7 @@ Zwei Dinge gehen dabei schief.
**Die Größe.** Bei 219 Zuschnitten und höchstens 82 Rollen sind das knapp 18 000 Binärvariablen — unangenehm, aber allein noch kein Hindernis.
-**Die Symmetrie.** Das ist das eigentliche Problem. Alle Mutterrollen sind **gleich**. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound`\index{Branch-and-Bound}`{=latex} weiß das nicht und arbeitet sie einzeln ab.
+**Die Symmetrie.** Das ist das eigentliche Problem. Alle Mutterrollen sind **gleich**. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}`\index{Branch-and-Bound}`{=latex} weiß das nicht und arbeitet sie einzeln ab.
> **⚠️ Woran man ein Symmetrieproblem erkennt**
>
@@ -11676,7 +11676,7 @@ Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:
> Fange mit wenigen Mustern an. Frage nach jeder Lösung, ob es ein **noch nicht bekanntes** Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.
-Die Frage „lohnt sich noch ein Muster?" beantworten die **Dualwerte** des Master-LPs. Der Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der Schattenpreis`\index{Schattenpreis}`{=latex} aus [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise): Er sagt, wie viele Rollen ein zusätzliches Stück der Breite $i$ kostet.
+Die Frage „lohnt sich noch ein Muster?" beantworten die **Dualwerte** des Master-LPs. Der Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} aus [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise): Er sagt, wie viele Rollen ein zusätzliches Stück der Breite $i$ kostet.

@@ -11702,7 +11702,7 @@ $$
>
> Die letzte Zeile ist der Grund, warum das Verfahren **exakt** ist und nicht heuristisch: Wenn kein Muster mehr lohnende reduzierte Kosten hat, ist die LP-Lösung über *allen* Mustern optimal — auch über den nie erzeugten.
-**Und wie findet man das beste neue Muster?** Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **Rucksackproblem**`\index{Rucksackproblem}`{=latex}:
+**Und wie findet man das beste neue Muster?** Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **[Rucksackproblem](#gloss:rucksackproblem){.glossar-link}**`\index{Rucksackproblem}`{=latex}:
| Rucksack ([Kapitel 6](#kap-milp)) | hier |
| --- | --- |
@@ -12198,7 +12198,7 @@ Und selbst dort, wo die Ersparnis null ist, liefert das Verfahren etwas, das die
## 10.10 Selbsttest {#sec:dekomposition-selbsttest}
-1. Erklären Sie Symmetrie in einem MILP an einem Beispiel aus Ihrem Arbeitsumfeld.
+1. Erklären Sie Symmetrie in einem [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} an einem Beispiel aus Ihrem Arbeitsumfeld.
2. Was ist beim Zuschnitt eine „Spalte", und was steht darin?
3. Woher kommen die Preise, mit denen das Teilproblem rechnet?
4. Warum ist das Verfahren exakt, obwohl es fast alle Muster nie ansieht?
@@ -12226,11 +12226,11 @@ Sechs Kapitel, sechs Werkzeuge. Jedes einzelne wurde im Zusammenhang gezeigt; hi
| Verfahren | Wofür es gebaut ist | Was Sie bekommen | Wo es aufhört | Kapitel |
| --- | --- | --- | --- | --- |
-| **LP** | teilbare Mengen, lineare Zusammenhänge | beweisbares Optimum **plus Schattenpreise** | sobald etwas ganzzahlig sein muss | [Kapitel 5](#kap-lp) |
-| **MILP** | Ja/Nein-Entscheidungen, Fixkosten, Logik | beweisbares Optimum, Gap als Fortschrittsmaß | Laufzeit wächst mit der Zahl der Binärvariablen | [Kapitel 6](#kap-milp) |
-| **CP-SAT**`\index{CP-SAT}`{=latex} | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | [Kapitel 7](#kap-cpsat) |
+| **[LP](#gloss:lineare-programmierung-lp){.glossar-link}** | teilbare Mengen, lineare Zusammenhänge | beweisbares Optimum **plus Schattenpreise** | sobald etwas ganzzahlig sein muss | [Kapitel 5](#kap-lp) |
+| **[MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}** | Ja/Nein-Entscheidungen, Fixkosten, Logik | beweisbares Optimum, Gap als Fortschrittsmaß | Laufzeit wächst mit der Zahl der Binärvariablen | [Kapitel 6](#kap-milp) |
+| **[CP-SAT](#gloss:cp-sat){.glossar-link}**`\index{CP-SAT}`{=latex} | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | [Kapitel 7](#kap-cpsat) |
| **Graphenalgorithmen** | Flüsse, Zuordnung, Touren | oft **polynomiell** statt exponentiell | nur, wenn die Struktur wirklich passt | [Kapitel 8](#kap-graphen) |
-| **Metaheuristiken** | wenn der exakte Solver aussteigt | eine gute Lösung in **fester Zeit** | keine Garantie, kein Beweis, keine Schranke | [Kapitel 9](#kap-metaheuristiken) |
+| **Metaheuristiken** | wenn der exakte [Solver](#gloss:solver){.glossar-link} aussteigt | eine gute Lösung in **fester Zeit** | keine Garantie, kein Beweis, keine Schranke | [Kapitel 9](#kap-metaheuristiken) |
| **Spaltengenerierung** | Modelle mit astronomisch vielen Variablen | beweisbares Optimum über nie erzeugte Spalten | lohnt nur bei bestimmter Instanzstruktur | [Kapitel 10](#kap-dekomposition) |
**Die Spalte, die am meisten wert ist, ist die vierte.** Ein Verfahren zu kennen heißt zu wissen, wo es aufhört — nicht, wofür es gedacht ist.
@@ -12239,8 +12239,8 @@ Sechs Kapitel, sechs Werkzeuge. Jedes einzelne wurde im Zusammenhang gezeigt; hi
| Behauptung | Gemessen | Wo |
| --- | --- | --- |
-| „Der exakte Solver ist immer besser." | Bei 500 Aufträgen liefert CP-SAT eine Lösung, die **5,2 % schlechter** ist als die Faustregel eines Meisters | [Kapitel 9](#kap-metaheuristiken) |
-| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus LNS **2 289** Minuten Rüstzeit`\index{Rüstzeit}`{=latex}; untere Schranke 1 768 | [Kapitel 9](#kap-metaheuristiken) |
+| „Der exakte Solver ist immer besser." | Bei 500 Aufträgen liefert [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} eine Lösung, die **5,2 % schlechter** ist als die Faustregel eines Meisters | [Kapitel 9](#kap-metaheuristiken) |
+| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus [LNS](#gloss:large-neighborhood-search-lns){.glossar-link} **2 289** Minuten [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex}; untere Schranke 1 768 | [Kapitel 9](#kap-metaheuristiken) |
| „Man muss alle Variablen aufschreiben." | 28 erzeugte Muster von **46 408** möglichen — und die Lösung ist beweisbar optimal | [Kapitel 10](#kap-dekomposition) |
| „Dekomposition lohnt sich immer." | Bei drei Stücken je Rolle **11 %** Ersparnis, bei sechs **nichts** | [Kapitel 10](#kap-dekomposition) |
| „Mit Seed ist der Lauf reproduzierbar." | Schon **zwei** Arbeiter liefern bei identischem Seed drei verschiedene Pläne zum selben Zielwert | [Abschnitt 7.7](#sec:cpsat-parallele-suche) |
@@ -12249,7 +12249,7 @@ Sechs Kapitel, sechs Werkzeuge. Jedes einzelne wurde im Zusammenhang gezeigt; hi
1. **Runden.** Die LP-Lösung ist keine Näherung der ganzzahligen Lösung — sie kann beliebig weit danebenliegen, und gerundet sogar unzulässig werden.
2. **Ein zu großes Big-M.** Es macht das Modell nicht falsch, sondern die Suche langsam und die Schranken wertlos. So klein wie zulässig, nie „sicherheitshalber groß".
-3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine Metaheuristik`\index{Metaheuristik}`{=latex} den exakten Solver schlägt, ist eine Eigenschaft **des Problems** und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
+3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine [Metaheuristik](#gloss:metaheuristik){.glossar-link}`\index{Metaheuristik}`{=latex} den exakten Solver schlägt, ist eine Eigenschaft **des Problems** und lässt sich messen. Wer ihn nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.
## Wenn Sie nur eines mitnehmen
@@ -12266,7 +12266,7 @@ Drei Kapitel dieses Teils behandeln genau diese drei Abweichungen. Welches Werkz

-> **🎯 Merksatz zum Diagramm** Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die **Nichtlinearität** das Problem, geht es um Konvexität`\index{Konvexität}`{=latex} — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die **Unsicherheit** das Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
+> **🎯 Merksatz zum Diagramm** Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die **Nichtlinearität** das Problem, geht es um [Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex} — und damit darum, ob Sie eine Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die **Unsicherheit** das Problem, geht es um etwas ganz anderes: Nicht der [Solver](#gloss:solver){.glossar-link} ist die Schwierigkeit, sondern die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
>
> Der häufigste Fehler an dieser Stelle ist, Unsicherheit durch **Mittelwerte** zu ersetzen und dann deterministisch zu rechnen. Warum das systematisch danebengeht, zeigt [Kapitel 12](#kap-unsicherheit) unter dem Namen *Fluch des Durchschnitts*.
@@ -12278,11 +12278,11 @@ Zwei weitere Kapitel schließen sich an, weil sie dieselbe Voraussetzung aufgebe
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der Polyeder`\index{Polyeder}`{=latex}. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in [Teil IV](#teil-anwendungen).
+> **Worum geht es?** Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man die Welt der [Polyeder](#gloss:polyeder){.glossar-link}`\index{Polyeder}`{=latex}. Dieses Kapitel liefert das mathematische Fundament für die gesamte Portfoliooptimierung in [Teil IV](#teil-anwendungen).
>
> **Voraussetzungen:** [Kapitel 2](#kap-fundament) (Konvexität, Eigenwerte), [Kapitel 5](#kap-lp) (Dualität, Schattenpreise). Gradienten werden in [Abschnitt 11.2](#sec:qp-nlp-lernziele) wiederholt.
>
-> **Danach können Sie:** Ein quadratisches Programm aufstellen, die KKT-Bedingungen anwenden, den Zusammenhang zwischen Lagrange-Multiplikator`\index{Lagrange-Multiplikator}`{=latex} und Schattenpreis erklären, eine gültige Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
+> **Danach können Sie:** Ein quadratisches Programm aufstellen, die [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}-Bedingungen anwenden, den Zusammenhang zwischen [Lagrange-Multiplikator](#gloss:lagrange-multiplikator){.glossar-link}`\index{Lagrange-Multiplikator}`{=latex} und [Schattenpreis](#gloss:schattenpreis){.glossar-link} erklären, eine gültige [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} konstruieren — und mit nicht-konvexen Problemen umgehen, ohne ein lokales Optimum für das Optimum zu halten.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
>
@@ -12354,7 +12354,7 @@ $$
\sigma_p^2 = w_A^2\sigma_A^2 + w_B^2\sigma_B^2 + 2\,w_A w_B \rho\,\sigma_A\sigma_B
$$
-Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er **negativ** und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus [Teil II](#teil-kernverfahren) — die Zielfunktion`\index{Zielfunktion}`{=latex} ist kein Skalarprodukt`\index{Skalarprodukt}`{=latex} mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY`\index{CVXPY}`{=latex} das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
+Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er **negativ** und zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus [Teil II](#teil-kernverfahren) — die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} ist kein [Skalarprodukt](#gloss:skalarprodukt){.glossar-link}`\index{Skalarprodukt}`{=latex} mehr, sondern eine quadratische Form. Was das für die Lösbarkeit bedeutet und warum [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} das trotzdem mit Optimalitätsgarantie löst, klärt der Rest des Kapitels.
> **🎯 Merksatz** `cp.quad_form(w, S)` ist die Zeile, um die es im ganzen Kapitel geht. Sie ist genau dann harmlos, wenn $\mathbf{S}$ **positiv semidefinit** ist — und genau dann ein Problem, wenn nicht. CVXPY prüft das für Sie und verweigert die Arbeit im Zweifelsfall. Diese Verweigerung ist ein Schutzmechanismus, keine Schikane.
@@ -12364,14 +12364,14 @@ Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er **negati
Nach diesem Kapitel können Sie …
-1. … ein **quadratisches Programm (QP)** aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
+1. … ein **quadratisches Programm ([QP](#gloss:quadratische-programmierung-qp){.glossar-link})** aufstellen und von einem allgemeinen nichtlinearen Programm abgrenzen.
2. … die **KKT-Bedingungen** herleiten und auf ein Optimierungsproblem anwenden.
3. … den Zusammenhang zwischen **Lagrange-Multiplikator** und **Schattenpreis**`\index{Schattenpreis}`{=latex} erklären.
4. … zwischen **konvex**`\index{Konvexität}`{=latex} und **streng konvex**`\index{Konvexität!streng konvex}`{=latex} unterscheiden und eine gültige, positiv semidefinite **Kovarianzmatrix**`\index{Kovarianzmatrix}`{=latex} konstruieren.
### Auffrischung: Gradient in einer Minute
-Der **Gradient**`\index{Gradient}`{=latex} $\nabla f(\mathbf{x})$ ist der Vektor aller partiellen Ableitungen:
+Der **[Gradient](#gloss:gradient){.glossar-link}**`\index{Gradient}`{=latex} $\nabla f(\mathbf{x})$ ist der Vektor aller partiellen Ableitungen:
$$
\nabla f(\mathbf{x}) = \left(\frac{\partial f}{\partial x_1},\ \frac{\partial f}{\partial x_2},\ \dots,\ \frac{\partial f}{\partial x_n}\right)^\top
@@ -12418,7 +12418,7 @@ Eine verbreitete, aber zu stark formulierte Aussage lautet: *„Wenn $\mathbf{P}
| $\mathbf{P} \succ 0$ — **positiv definit**, alle $\lambda_i > 0$ | Echte Schüssel ohne flache Richtung | Problem ist **streng konvex**. Die Minimalstelle ist **eindeutig** |
| $\mathbf{P}$ hat einen negativen Eigenwert | Sattel oder Rinne nach unten | Problem ist **nicht konvex**. Lokale Optima möglich, keine Garantie |
-**Warum das praktisch zählt:** Eine Stichproben-Kovarianzmatrix`\index{Stichproben-Kovarianzmatrix}`{=latex} mit mehr Titeln als Beobachtungen ($N > T$) ist **singulär** — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus [Kapitel 18](#kap-finanzdaten).
+**Warum das praktisch zählt:** Eine [Stichproben-Kovarianzmatrix](#gloss:stichproben-kovarianzmatrix){.glossar-link}`\index{Stichproben-Kovarianzmatrix}`{=latex} mit mehr Titeln als Beobachtungen ($N > T$) ist **singulär** — also nur semidefinit. Dann gibt es unendlich viele Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon, scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau dieses Problem behebt die Shrinkage aus [Kapitel 18](#kap-finanzdaten).
Alle drei Zeilen der Tabelle lassen sich an einem winzigen Zwei-Variablen-QP direkt beobachten — inklusive der Stelle, an der CVXPY ein nicht konvexes Problem **verweigert**, bevor überhaupt ein Solver aufgerufen wird:
@@ -12514,7 +12514,7 @@ Der letzte Block ist der eigentliche Beweis: Drei völlig verschiedene Portfolio
> ```python
> eigenwerte = np.linalg.eigvalsh(Sigma) # 'h' = fuer symmetrische Matrizen
> assert np.all(eigenwerte >= -1e-10), "Sigma ist nicht positiv semidefinit!"
-> print(f"Konditionszahl: {eigenwerte.max() / eigenwerte.min():.1f}")
+> print(f"[Konditionszahl](#gloss:konditionszahl){.glossar-link}: {eigenwerte.max() / eigenwerte.min():.1f}")
> ```
> Eine einzige Zeile — und sie deckt eine ungültige Kovarianzmatrix sofort auf.
@@ -12530,7 +12530,7 @@ $$
\min f(\mathbf{x}) \quad\text{u. d. N.}\quad g_i(\mathbf{x})\le0\ (i=1..m),\qquad h_j(\mathbf{x})=0\ (j=1..p)
$$
-lautet die **Lagrange-Funktion**`\index{Lagrange-Funktion}`{=latex}:
+lautet die **[Lagrange-Funktion](#gloss:lagrange-funktion){.glossar-link}**`\index{Lagrange-Funktion}`{=latex}:
$$
\mathcal{L}(\mathbf{x},\boldsymbol{\lambda},\boldsymbol{\nu})
@@ -12559,7 +12559,7 @@ $$
*Warum?* Eine Wand kann nur **drücken**, nicht ziehen. Ein negatives $\lambda$ hieße, die Bedingung würde die Lösung von sich weg**ziehen** — dann wäre sie nicht bindend.
-**4. Komplementärer Schlupf**`\index{Komplementärer Schlupf}`{=latex} — $\lambda_i^*\cdot g_i(\mathbf{x}^*)=0$ für alle $i$.
+**4. [Komplementärer Schlupf](#gloss:komplementaerer-schlupf){.glossar-link}**`\index{Komplementärer Schlupf}`{=latex} — $\lambda_i^*\cdot g_i(\mathbf{x}^*)=0$ für alle $i$.
*Bedeutung:* Entweder ist die Bedingung nicht bindend ($g_i<0$), **dann muss** $\lambda_i=0$ sein. Oder der Multiplikator ist positiv, **dann muss** die Bedingung mit Gleichheit binden. Das ist derselbe Satz wie in [Kapitel 5](#kap-lp) — nur allgemeiner formuliert.
@@ -12576,7 +12576,7 @@ lässt nur $x \le 3$ zu, und $\lambda^* = 4$ ist genau die Steigung von $f$ am R
> | $g_i(\mathbf{x}^*) \le 0$ | „Der Plan hält alle Regeln ein.“ |
> | $\lambda_i^* \ge 0$ | „Eine Grenze kann nur bremsen, nie antreiben.“ Ein negativer Preis wäre widersinnig. |
> | $\lambda_i^* \cdot g_i(\mathbf{x}^*) = 0$ | „Was Reserve hat, ist nichts wert. Was etwas wert ist, ist ausgereizt.“ Nie beides. |
-> | $\lambda_i^*$ selbst | Der **Schattenpreis** von Bedingung $i$ — dieselbe Größe wie im LP ([Kapitel 5](#kap-lp)), nur für krumme Ränder. |
+> | $\lambda_i^*$ selbst | Der **Schattenpreis** von Bedingung $i$ — dieselbe Größe wie im [LP](#gloss:lineare-programmierung-lp){.glossar-link} ([Kapitel 5](#kap-lp)), nur für krumme Ränder. |
>
> **Alle vier zusammen in einem Satz:** *Wir stehen an einer Stelle, an der jede Verbesserung an eine Grenze stößt — und wir wissen für jede dieser Grenzen, was ihre Lockerung wert wäre.*
>
@@ -12717,13 +12717,13 @@ if __name__ == "__main__":
## 11.5 Nichtlineare Optimierung mit `scipy.optimize.minimize` {#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
-Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **SLSQP**`\index{SLSQP}`{=latex} (*Sequential Least Squares Programming*).
+Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **[SLSQP](#gloss:slsqp){.glossar-link}**`\index{SLSQP}`{=latex} (*Sequential Least Squares Programming*).
> **⚠️ Der entscheidende Unterschied zu CVXPY** CVXPY`\index{CVXPY}`{=latex} **prüft** die Konvexität und garantiert bei Erfolg das globale Optimum. `scipy.optimize.minimize` prüft nichts und liefert ein **lokales** Optimum, das vom Startpunkt abhängt (siehe die Demonstration in [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)). Wer SLSQP`\index{SLSQP}`{=latex} einsetzt, sollte **immer mit mehreren Startpunkten rechnen** und die Ergebnisse vergleichen.
### Praxisfall: Entropie-maximierte Kapitalallokation
-Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die **Shannon-Entropie**`\index{Shannon-Entropie}`{=latex} der Gewichte, um Klumpenrisiken glatt zu bestrafen:
+Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die **[Shannon-Entropie](#gloss:shannon-entropie){.glossar-link}**`\index{Shannon-Entropie}`{=latex} der Gewichte, um Klumpenrisiken glatt zu bestrafen:
$$
\min_{\mathbf{w}}\quad \underbrace{\tfrac12\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}_{\text{Risiko}}
@@ -12943,7 +12943,7 @@ Nicht aus exotischer Mathematik, sondern aus ganz gewöhnlichen betriebswirtscha
| **Mengenrabatte** | Stückpreis fällt mit der Bestellmenge | Kostenfunktion wird konkav — Sprünge nach unten belohnen große Lose |
| **Skaleneffekte** | Stückkosten sinken mit der Losgröße | dasselbe Muster in der Produktion |
| **Produkte von Variablen** | „Menge`\index{Konvexität!Menge}`{=latex} mal Preis“, wenn beide entschieden werden | $x \cdot y$ ist weder konvex noch konkav |
-| **Ja/Nein mal Menge** | Anlage läuft *und* wie stark | ganzzahlig + nichtlinear = MINLP |
+| **Ja/Nein mal Menge** | Anlage läuft *und* wie stark | ganzzahlig + nichtlinear = [MINLP](#gloss:minlp){.glossar-link} |
| **Verhältnisse** | Auslastungsgrad, Rendite je eingesetztem Euro | Quotienten sind selten konvex |
> **🎯 Merksatz** Sobald ein **Rabatt**, ein **Skaleneffekt** oder ein **Produkt zweier Entscheidungen** im Modell steht, ist die Konvexität in Gefahr — und damit die Optimalitätsgarantie. Das ist kein Grund zur Panik, aber ein Grund, das Ergebnis anders zu behandeln.
@@ -13243,9 +13243,9 @@ Wird zusätzlich noch ganzzahlig entschieden — „welche Anlage läuft überha
| Werkzeug | Art | Was es leistet |
| --- | --- | --- |
-| **Ipopt** (über `cyipopt` oder Pyomo`\index{Pyomo}`{=latex}) | lokaler NLP-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
+| **Ipopt** (über `cyipopt` oder [Pyomo](#gloss:pyomo){.glossar-link}`\index{Pyomo}`{=latex}) | lokaler [NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link}-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
| **Bonmin**, **Couenne** | MINLP | Bonmin lokal, Couenne mit globaler Garantie für viele Klassen |
-| **SCIP**`\index{SCIP}`{=latex}, **BARON**, **Gurobi**`\index{Gurobi}`{=latex} | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
+| **[SCIP](#gloss:scip){.glossar-link}**`\index{SCIP}`{=latex}, **BARON**, **[Gurobi](#gloss:gurobi){.glossar-link}**`\index{Gurobi}`{=latex} | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
| **Pyomo** ([Abschnitt 3.7](#sec:oekosystem-pyomo-linopy)) | Modellierungsschicht | Bindet alle oben genannten an, ohne dass das Modell umgeschrieben werden muss |
> **⚠️ Zu diesem Abschnitt gehört kein lauffähiges Programm.** Ipopt, Bonmin und Couenne sind C++-Pakete, die über die Python-Installation hinaus systemweit eingerichtet werden müssen (`conda install -c conda-forge ipopt cyipopt` oder eine Distributionspaketquelle). Anders als bei allen übrigen Programmen dieses Buches konnten wir den Code deshalb nicht auf jedem Zielsystem ausführen — und drucken hier bewusst **keine** Ausgabe ab, die wir nicht selbst erzeugt haben. Der folgende Ausschnitt zeigt die Anbindung; prüfen Sie das Ergebnis auf Ihrem System selbst nach.
@@ -13290,7 +13290,7 @@ else:
**Aufgabe 11.4 ⭐⭐ — Ungültige Kovarianzmatrix erkennen.** Prüfen Sie, ob folgende Korrelationsmatrix möglich ist, und begründen Sie: $$\mathbf{C} = \begin{pmatrix}1{,}0&0{,}9&-0{,}9\\0{,}9&1{,}0&0{,}9\\-0{,}9&0{,}9&1{,}0\end{pmatrix}$$ (Tipp: Wenn A stark mit B korreliert und B stark mit C, kann A dann stark **negativ** mit C korrelieren?)
-**Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen.** Variieren Sie in `Entropie_Maximierte_Allokation.py` systematisch $\alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\}$ und $\beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}$. Stellen Sie für jede Kombination Rendite, Volatilität`\index{Volatilität}`{=latex} und Entropie in einer Tabelle dar. (a) Wie verändert $\beta$ die Konzentration im Krypto-Titel? (b) Ab welchem $\beta$ nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
+**Aufgabe 11.5 ⭐⭐⭐ — Effekt der Gewichtung untersuchen.** Variieren Sie in `Entropie_Maximierte_Allokation.py` systematisch $\alpha \in \{0{,}1;\ 0{,}5;\ 1;\ 5\}$ und $\beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}$. Stellen Sie für jede Kombination Rendite, [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex} und Entropie in einer Tabelle dar. (a) Wie verändert $\beta$ die Konzentration im Krypto-Titel? (b) Ab welchem $\beta$ nähert sich die Lösung der Gleichgewichtung? (c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem Anlageausschuss vorlegen?
**Aufgabe 11.6 ⭐⭐⭐ — Nicht-Konvexität demonstrieren.** Bauen Sie bewusst eine ungültige „Kovarianzmatrix“ (per A@A.T mit anschließend überschriebener Diagonale) und lösen Sie das Modell mit 20 Startpunkten. Dokumentieren Sie: Wie viele verschiedene Optima entstehen? Wie groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die „Portfoliovarianz“ negativ werden?
@@ -13311,7 +13311,7 @@ else:
> Sein erster Versuch mit CVXPY bricht ab:
>
> ```
-> DCPError: Problem does not follow DCP rules.
+> [DCPError](#gloss:dcperror){.glossar-link}: Problem does not follow [DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link} rules.
> The objective is not DCP. Its following subexpressions are not: QuadForm(...)
> ```
>
@@ -13386,7 +13386,7 @@ else:
* **Nicht-Konvexität kommt aus dem Alltag**, nicht aus der Theorie: Mengenrabatte, Skaleneffekte, Produkte zweier Entscheidungen. Wo eines davon im Modell steht, ist die Optimalitätsgarantie weg.
* **Berichten Sie bei nicht-konvexen Problemen die Streuung mit**, nicht die letzte Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein einzelner Wert ist es nicht.
-**Ausblick.** [Kapitel 12](#kap-unsicherheit) gibt die Annahme auf, dass die Parameter`\index{Parameter (Modell)}`{=latex} überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
+**Ausblick.** [Kapitel 12](#kap-unsicherheit) gibt die Annahme auf, dass die [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} überhaupt bekannt sind. Wir lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
---
@@ -13395,7 +13395,7 @@ else:
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Bisher waren alle Parameter`\index{Parameter (Modell)}`{=latex} bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
+> **Worum geht es?** Bisher waren alle [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
>
> **Voraussetzungen:** [Kapitel 5](#kap-lp), [Kapitel 11](#kap-qp-nlp). Grundbegriffe der Statistik (Erwartungswert, Quantil).
>
@@ -13477,7 +13477,7 @@ Das Quantil zu 0,921 liegt bei genau **38 Stück** — dasselbe Ergebnis, das di
> **🎯 Merksatz** Die optimale Entscheidung unter Unsicherheit ist fast nie die Entscheidung, die für den Mittelwert optimal wäre. Sie hängt davon ab, **welcher der beiden Fehler teurer ist** — und verschiebt sich zu der Seite, auf der Irren billiger ist. Wer mit dem Mittelwert plant, hat diese Frage nie gestellt.
-Dieses Muster heißt **Newsvendor-Problem**`\index{Newsvendor-Problem}`{=latex} (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
+Dieses Muster heißt **[Newsvendor-Problem](#gloss:newsvendor-problem){.glossar-link}**`\index{Newsvendor-Problem}`{=latex} (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.
---
@@ -13488,8 +13488,8 @@ Nach diesem Kapitel können Sie …
1. … den **Fluch des Durchschnitts**`\index{Fluch des Durchschnitts}`{=latex} (*Flaw of Averages*) an einem Beispiel erklären.
2. … ein **zweistufiges stochastisches Programm** mit Recourse formulieren.
3. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
-4. … ein **robustes** Modell mit Unsicherheitsmenge`\index{Unsicherheitsmenge}`{=latex} aufstellen und seinen Preis beziffern.
-5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint`\index{Chance Constraint}`{=latex} schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
+4. … ein **robustes** Modell mit [Unsicherheitsmenge](#gloss:unsicherheitsmenge){.glossar-link}`\index{Unsicherheitsmenge}`{=latex} aufstellen und seinen Preis beziffern.
+5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als [Chance Constraint](#gloss:chance-constraint){.glossar-link}`\index{Chance Constraint}`{=latex} schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
6. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
7. … das **kritische Verhältnis** eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen.
8. … eine Terminzusage als Quantil formulieren statt als Mittelwert.
@@ -13617,7 +13617,7 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.
Kein Rechentrick, sondern brute-force über alle ganzzahligen $x$ von 0 bis 500 — das Optimum landet exakt dort, wo es die Handrechnung vorhersagt.
-**Warum?** Die Kostenfunktion ist **asymmetrisch**: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die **Jensensche Ungleichung**`\index{Jensensche Ungleichung}`{=latex}: Für konvexe Kostenfunktionen gilt $\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])$ — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
+**Warum?** Die Kostenfunktion ist **asymmetrisch**: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die **[Jensensche Ungleichung](#gloss:jensensche-ungleichung){.glossar-link}**`\index{Jensensche Ungleichung}`{=latex}: Für konvexe Kostenfunktionen gilt $\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])$ — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
> **🎯 Merksatz** Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt.
@@ -13625,7 +13625,7 @@ Kein Rechentrick, sondern brute-force über alle ganzzahligen $x$ von 0 bis 500

-| | **Monte-Carlo** | **Stochastische Programmierung**`\index{Stochastische Programmierung}`{=latex} | **Robuste Optimierung**`\index{Robuste Optimierung}`{=latex} | **Chance Constraints** |
+| | **Monte-Carlo** | **[Stochastische Programmierung](#gloss:stochastische-programmierung){.glossar-link}**`\index{Stochastische Programmierung}`{=latex} | **[Robuste Optimierung](#gloss:robuste-optimierung){.glossar-link}**`\index{Robuste Optimierung}`{=latex} | **Chance Constraints** |
| --- | --- | --- | --- | --- |
| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge**`\index{Konvexität!Menge}`{=latex} möglicher Werte | Verteilung **oder** Szenarien |
| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | Kosten bei **zugesicherter Quote** |
@@ -13968,7 +13968,7 @@ deutlich teurer ist als Leerlauf (5 EUR).
**Lesen Sie die Vergleichstabelle von unten nach oben.** Die beiden extremen Haltungen sind die teuersten: Wer den Worst Case abdeckt (500 Einheiten), zahlt 5 000 € zu viel für Kapazität, die in 80 % der Fälle brachliegt. Wer optimistisch plant (100), zahlt 2 625 € Strafe für ständige Notzukäufe. Die naive Mittelwertplanung liegt dazwischen — aber eben auch nicht optimal.
-Der **EVPI von 7 375 €** ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe *EVPI interpretieren* ([Abschnitt 12.8](#sec:unsicherheit-uebungsaufgaben)).
+Der **[EVPI](#gloss:evpi-expected-value-of-perfect-information){.glossar-link} von 7 375 €** ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe *EVPI interpretieren* ([Abschnitt 12.8](#sec:unsicherheit-uebungsaufgaben)).
> **💡 Was ist der EVPI?** Der **Expected Value of Perfect Information**`\index{EVPI}`{=latex} beziffert, wie viel eine perfekte Prognose wert wäre: die Differenz zwischen den Kosten unter Unsicherheit und den Kosten bei vollständigem Wissen. Er ist eine **Obergrenze für jedes Prognoseprojekt**. Wenn der EVPI bei 12 000 € pro Jahr liegt, lohnt sich keine Prognosesoftware für 50 000 € — selbst wenn sie perfekt wäre. Diese Zahl bewahrt Projekte vor teuren Fehlinvestitionen.
@@ -14102,7 +14102,7 @@ if __name__ == "__main__":
> **⚠️ Der Preis der Robustheit** Robuste Modelle sind **konservativ**. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel:
> 1. **Unsicherheitsmenge realistisch wählen.** Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“.
-> 2. **Budgeted Uncertainty (Bertsimas/Sim)**`\index{Budgeted Uncertainty}`{=latex}: Man nimmt an, dass höchstens $\Gamma$ von $n$ Parametern gleichzeitig ihren Worst Case annehmen. $\Gamma$ steuert die Vorsicht stufenlos.
+> 2. **[Budgeted Uncertainty](#gloss:budgeted-uncertainty){.glossar-link} (Bertsimas/Sim)**`\index{Budgeted Uncertainty}`{=latex}: Man nimmt an, dass höchstens $\Gamma$ von $n$ Parametern gleichzeitig ihren Worst Case annehmen. $\Gamma$ steuert die Vorsicht stufenlos.
> 3. **Nutzen und Preis immer beziffern** — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.
---
@@ -14124,7 +14124,7 @@ $$
>
> **Ohne Formel gesagt:** „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“
-**Das Problem daran.** So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
+**Das Problem daran.** So aufgeschrieben ist die Bedingung nicht lösbar: Ein [Solver](#gloss:solver){.glossar-link} kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.
### Weg 1: Normalverteilung — die Bedingung wird ein Kegel
@@ -14138,9 +14138,9 @@ $$
z_{1-\alpha} = \Phi^{-1}(1-\alpha)
$$
-Das ist eine **Second-Order-Cone-Bedingung**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} — dieselbe Bauform, die [Kapitel 11](#kap-qp-nlp) eingeführt hat, und CVXPY`\index{CVXPY}`{=latex} löst sie ohne Umstände.
+Das ist eine **Second-Order-Cone-Bedingung**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} — dieselbe Bauform, die [Kapitel 11](#kap-qp-nlp) eingeführt hat, und [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} löst sie ohne Umstände.
-> **🎯 Warum eine Norm und keine Summe** Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation`\index{Diversifikation}`{=latex} nicht belohnen.
+> **🎯 Warum eine Norm und keine Summe** Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde [Diversifikation](#gloss:diversifikation){.glossar-link}`\index{Diversifikation}`{=latex} nicht belohnen.
### Weg 2: Szenarien — Big-M ohne Verteilungsannahme
@@ -14154,7 +14154,7 @@ z_s &\in \{0,1\}
\end{aligned}
$$
-Aus dem Kegelproblem wird ein MILP mit $S$ Binärvariablen. Für $M$ gilt, was [Kapitel 6](#kap-milp) über die Big-M-Falle sagt: **so klein wie möglich**. Hier ist $M = b$ die kleinste gültige Schranke, denn $\mathbf{a}_s^\top\mathbf{x} \ge 0$ — größer kann eine Verletzung gar nicht ausfallen.
+Aus dem Kegelproblem wird ein [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} mit $S$ Binärvariablen. Für $M$ gilt, was [Kapitel 6](#kap-milp) über die Big-M-Falle sagt: **so klein wie möglich**. Hier ist $M = b$ die kleinste gültige Schranke, denn $\mathbf{a}_s^\top\mathbf{x} \ge 0$ — größer kann eine Verletzung gar nicht ausfallen.
### Der Fall: ein Kraftwerkspark mit 500 MW Zusage
@@ -14462,7 +14462,7 @@ Szenarien, 95 % 43,760,709 350 0 0 350 100.00 % 94.84 %
**Zweitens: Sicherheit ist konvex bepreist.** Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das **4,5-fache**. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der *nächste* Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.
-**Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} mit Korrelationen um null kann so etwas nicht ausdrücken.
+**Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} mit Korrelationen um null kann so etwas nicht ausdrücken.
Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf 94,8 %, kostet dafür 16,7 % mehr — und kauft **keine Windkraft mehr**. Die Logik dahinter ist hart`\index{Nebenbedingung!hart}`{=latex} und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
@@ -14470,7 +14470,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
>
> Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei $S = 200$, $400$, $800$, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen **93,1 % und 96,7 %**. Die Spanne wurde von $S = 200$ auf $400$ enger (3,5 → 1,2 Prozentpunkte), bei $S = 800$ aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht.
>
-> **Konsequenz für die Praxis:** Eine mit Szenarien erkaufte Zusage immer an einer **zurückgehaltenen** Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in [Kapitel 21](#kap-handelsmaschine).
+> **Konsequenz für die Praxis:** Eine mit Szenarien erkaufte Zusage immer an einer **zurückgehaltenen** Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim [Backtest](#gloss:backtest){.glossar-link} in [Kapitel 21](#kap-handelsmaschine).
> **🎯 Merksatz** Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern.
@@ -14488,7 +14488,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
**Aufgabe 12.4 ⭐⭐ — EVPI interpretieren.** Ihr Modell liefert EVPI = 8 400 € pro Jahr. Ein Anbieter verlangt 15 000 € jährlich für eine Prognoselösung, die „80 % Treffsicherheit“ verspricht. Wie argumentieren Sie?
-**Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern.** Erweitern Sie `Monte_Carlo.py`: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
+**Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern.** Erweitern Sie `Monte_Carlo.py`: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.
**Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen.** `Chance_Constraints.py` beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das **szenariobasierte** Modell meldet dann bei 95 % `infeasible`, das **analytische** dagegen `optimal`. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten.
@@ -14546,7 +14546,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
>
> **2. Was unterscheidet **stochastische** von **robuster** Optimierung?** (a) Stochastische Optimierung ist genauer, robuste ist eine Näherung für schnelle Rechnungen. (b) Stochastische Optimierung braucht **Wahrscheinlichkeiten** und optimiert den Erwartungswert; robuste Optimierung braucht nur **Bandbreiten** und sichert den schlechtesten Fall darin ab. (c) Robuste Optimierung berücksichtigt mehr Szenarien.
>
-> **3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch?** (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion`\index{Zielfunktion}`{=latex} und Nebenbedingungen in den unsicheren Größen **linear** sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
+> **3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch?** (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} und Nebenbedingungen in den unsicheren Größen **linear** sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
---
@@ -14577,7 +14577,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
* **Eine Zusage gilt nur für die unterstellte Verteilung.** Ein Plan, der unter Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle nicht kannte, in der *alles* zugleich ausfällt. Szenariobasierte Zusagen wiederum überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden.
* **Beziffern Sie immer Preis und Nutzen der Absicherung.** Ohne beide Zahlen ist die Entscheidung nicht begründbar.
-**Ausblick.** [Kapitel 13](#kap-dynamische-programmierung) fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung`\index{Bellman-Gleichung}`{=latex}.
+**Ausblick.** [Kapitel 13](#kap-dynamische-programmierung) fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der [Bellman-Gleichung](#gloss:bellman-gleichung){.glossar-link}`\index{Bellman-Gleichung}`{=latex}.
---
@@ -14586,11 +14586,11 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.
+> **Worum geht es?** Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die [Bellman-Gleichung](#gloss:bellman-gleichung){.glossar-link} löst solche Probleme durch Rückwärtsrechnen.
>
> **Voraussetzungen:** [Kapitel 12](#kap-unsicherheit) (Erwartungswerte). Rekursion in Python.
>
-> **Danach können Sie:** Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion`\index{Rückwärtsinduktion}`{=latex} berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
+> **Danach können Sie:** Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch [Rückwärtsinduktion](#gloss:rueckwaertsinduktion){.glossar-link}`\index{Rückwärtsinduktion}`{=latex} berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.
>
> **Zeitbedarf:** ca. 5 Stunden.
>
@@ -14655,7 +14655,7 @@ Der Grund steht in der zweiten Ausgabezeile. `Restkosten je Ort` ist die **Wertf
> **🎯 Merksatz** Eine Entscheidung ist nie für sich zu bewerten, sondern nur **zusammen mit allem, was danach kommt**. Genau das leistet die Wertfunktion: Sie fasst die gesamte Zukunft eines Zustands in einer einzigen Zahl zusammen. Und weil man die Zukunft kennen muss, bevor man die Gegenwart bewerten kann, rechnet dynamische Programmierung **rückwärts**.
-**Warum funktioniert das?** Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt **Bellmansches Optimalitätsprinzip**`\index{Optimalitätsprinzip}`{=latex} und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
+**Warum funktioniert das?** Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt **Bellmansches [Optimalitätsprinzip](#gloss:optimalitaetsprinzip){.glossar-link}**`\index{Optimalitätsprinzip}`{=latex} und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
---
@@ -14667,9 +14667,9 @@ Nach diesem Kapitel können Sie …
2. … Zustand, Stufe, Aktion und Wertfunktion für ein gegebenes Problem benennen.
3. … eine Rückwärtsinduktion`\index{Rückwärtsinduktion}`{=latex} von Hand für ein kleines Problem durchführen.
4. … das Almgren-Chriss-Ausführungsproblem lösen und die Lösung interpretieren.
-5. … einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität`\index{Fluch der Dimensionalität}`{=latex}“ zuschlägt.
+5. … einschätzen, wann [DP](#gloss:dynamische-programmierung-dp){.glossar-link} funktioniert und wann der „[Fluch der Dimensionalität](#gloss:fluch-der-dimensionalitaet){.glossar-link}`\index{Fluch der Dimensionalität}`{=latex}“ zuschlägt.
6. … die Wertfunktion als **Nachschlagetabelle** lesen: Sie liefert keine Planfolge, sondern eine Regel, die auch bei Abweichungen gilt.
-7. … einen Zustandsraum`\index{Zustandsraum}`{=latex} auf Vollständigkeit prüfen und erkennen, wann eine Kostenart Vorgeschichte erfordert.
+7. … einen [Zustandsraum](#gloss:zustandsraum){.glossar-link}`\index{Zustandsraum}`{=latex} auf Vollständigkeit prüfen und erkennen, wann eine Kostenart Vorgeschichte erfordert.
---
@@ -14869,7 +14869,7 @@ $$
> * $\eta$ („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen?
> * $X_t$ — Restbestand **nach** dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst.
> * $\lambda$ — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage?
-> * $\sigma$ — Volatilität`\index{Volatilität}`{=latex} je Periode.
+> * $\sigma$ — [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex} je Periode.
>
> **Ohne Formel gesagt:** „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet Nervenkitzel. Finde die Mitte.“
>
@@ -14883,7 +14883,7 @@ $$
> ```python
> holding_risk = risk_aversion * (sigma_period ** 2) * (remaining ** 2) * 1e5
> ```
-> Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter`\index{Parameter (Modell)}`{=latex} wie `risk_aversion = 1e-6` würde dann in Wahrheit $0{,}1$ bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
+> Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} wie `risk_aversion = 1e-6` würde dann in Wahrheit $0{,}1$ bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.
>
> **Der Parameter lässt sich stattdessen herleiten.** Beide Kostenterme werden in **Euro** gerechnet:
> * Marktauswirkung: $\eta \cdot n_t^2$ mit $\eta$ in €/Stück².
@@ -15153,7 +15153,7 @@ Abweichung DP zur analytischen Loesung: 5.788 % (Diskretisierung: 1000 Stueck)
**Die Lösung ist *front-loaded*:** 41 % im ersten Schritt, dann fallend (25 %, 16 %, 10 %, 8 %). Das ist die typische Form — man baut Risiko früh ab, aber nicht abrupt. Die Sensitivitätstabelle zeigt, wie $\lambda$ zwischen den beiden Extremen steuert: Bei $\lambda = 10^{-7}$ verkauft das Modell gleichmäßig (Risiko ist egal), bei $\lambda = 10^{-3}$ praktisch alles sofort (Risiko dominiert).
-Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie`\index{TWAP}`{=latex} (gleiche Tranchen) spart die optimierte Ausführung **2 976 €** oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
+Der Vergleich beziffert den Nutzen: Gegenüber der naiven [TWAP](#gloss:twap){.glossar-link}-Strategie`\index{TWAP}`{=latex} (gleiche Tranchen) spart die optimierte Ausführung **2 976 €** oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.
> **💻 Code-Durchgang: die analytische Gegenprobe und was ihre Abweichung bedeutet**
>
@@ -15193,8 +15193,8 @@ Das Problem entsteht, sobald der Zustand **mehrere Dimensionen** hat:
**Gegenmittel:**
* **Zustandsraum**`\index{Zustandsraum}`{=latex} **verkleinern:** gröber diskretisieren, irrelevante Variablen weglassen.
-* **Approximate Dynamic Programming**`\index{Dynamische Programmierung!Approximate Dynamic Programming}`{=latex}**:** $V_t$ durch eine parametrische Funktion`\index{Konvexität!Funktion}`{=latex} annähern statt tabellarisch zu speichern.
-* **Reinforcement Learning**`\index{Reinforcement Learning}`{=latex}**:** dieselbe Bellman-Gleichung`\index{Bellman-Gleichung}`{=latex}, aber $V$ wird aus Erfahrung gelernt (Q-Learning`\index{Q-Learning}`{=latex}) statt vollständig berechnet.
+* **[Approximate Dynamic Programming](#gloss:approximate-dynamic-programming){.glossar-link}**`\index{Dynamische Programmierung!Approximate Dynamic Programming}`{=latex}**:** $V_t$ durch eine parametrische Funktion`\index{Konvexität!Funktion}`{=latex} annähern statt tabellarisch zu speichern.
+* **[Reinforcement Learning](#gloss:reinforcement-learning){.glossar-link}**`\index{Reinforcement Learning}`{=latex}**:** dieselbe Bellman-Gleichung`\index{Bellman-Gleichung}`{=latex}, aber $V$ wird aus Erfahrung gelernt ([Q-Learning](#gloss:q-learning){.glossar-link}`\index{Q-Learning}`{=latex}) statt vollständig berechnet.
* **Nach geschlossenen Lösungen suchen** — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.
---
@@ -15209,7 +15209,7 @@ Das Problem entsteht, sobald der Zustand **mehrere Dimensionen** hat:
**Aufgabe 13.3 ⭐⭐ — Rückwärtsinduktion von Hand.** 5 Einheiten in 3 Perioden, Kosten $C(n) = n^2 + 2n$. Erstellen Sie die vollständige Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit `Bellman_Minimalbeispiel.py` (angepasst).
-**Aufgabe 13.4 ⭐⭐ — Rucksackproblem`\index{Rucksackproblem}`{=latex} als DP.** Lösen Sie das Rucksackproblem aus [Kapitel 6](#kap-milp) mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.
+**Aufgabe 13.4 ⭐⭐ — [Rucksackproblem](#gloss:rucksackproblem){.glossar-link}`\index{Rucksackproblem}`{=latex} als DP.** Lösen Sie das Rucksackproblem aus [Kapitel 6](#kap-milp) mit dynamischer Programmierung statt [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-[Solver](#gloss:solver){.glossar-link}.
**Aufgabe 13.5 ⭐⭐⭐ — Risikoaversion kalibrieren.** Untersuchen Sie mit `Mehrperiodige_Order_Execution.py`: (a) Bei welchem $\lambda$ verkauft das Modell in der ersten Periode mehr als 50 %? (b) Stellen Sie den Zusammenhang zwischen $\lambda$ und den erwarteten Gesamtkosten dar. (c) Ein Händler sagt: „Ich will höchstens 20 % Marktauswirkungskosten und den Rest an Risiko.“ Welches $\lambda$ setzen Sie?
@@ -15312,7 +15312,7 @@ Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder
>
> **Voraussetzungen:** [Kapitel 4](#kap-modellierung) (die Zielgröße als Entscheidung) und [Kapitel 6](#kap-milp). Für den Denkfehler hilft [Kapitel 19](#kap-markowitz).
>
-> **Danach können Sie:** begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige Pareto-Front`\index{Pareto-Front}`{=latex} mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
+> **Danach können Sie:** begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige [Pareto-Front](#gloss:pareto-front){.glossar-link}`\index{Pareto-Front}`{=latex} mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.
>
> **Zeitbedarf:** ca. 4 Stunden.
>
@@ -15333,7 +15333,7 @@ Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder
>
> ```python
> import numpy as np
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> # LKW Bahn Kombi LKW billig+schmutzig, Bahn teuer+sauber
> kosten = np.array([[ 900, 1150, 1020], [ 620, 790, 700], [1300, 1660, 1470],
@@ -15829,11 +15829,11 @@ Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-o
Das Programm rechnet diese Hülle in `untere_huelle()` unabhängig aus und vergleicht: Genau die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe steht in der Ausgabe.
-**Was Sie in der Abbildung sehen.** Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht $w$. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in [Abschnitt 14.7](#sec:mehrziel-epsilon) geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
+**Was Sie in der Abbildung sehen.** Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht $w$. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der [Solver](#gloss:solver){.glossar-link} als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in [Abschnitt 14.7](#sec:mehrziel-epsilon) geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
> **⚠️ Der Grund liegt in der Ganzzahligkeit**
>
-> Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen **ganzzahlig** werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
+> Bei einem reinen [LP](#gloss:lineare-programmierung-lp){.glossar-link} ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen **ganzzahlig** werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
>
> Deshalb betrifft dieses Problem praktisch jedes betriebliche Mehrzielmodell. Wer mit Gewichten arbeitet, verliert nicht ein paar Nachkommastellen, sondern **ganze Alternativen** — im Beispiel 40 % der Front.
@@ -15892,7 +15892,7 @@ $$
{\text{eingesparte Kilogramm}}
$$
-Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.
+Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der [Schattenpreis](#gloss:schattenpreis){.glossar-link} darunter, ist die Entscheidung kaufmännisch schon getroffen.
> **💻 Code-Durchgang: die drei Bausteine**
>
@@ -15916,7 +15916,7 @@ Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem in
**Aufgabe 14.3 ⭐⭐ — Mehr Trassen.** Erhöhen Sie `BAHN_TRASSEN` von 5 auf 8. Was passiert mit der Länge der Front und mit der Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.
-**Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation`\index{Relaxation}`{=latex}.** Lassen Sie `integrality` weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
+**Aufgabe 14.4 ⭐⭐ — Die Front der [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex}.** Lassen Sie `integrality` weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?
**Aufgabe 14.5 ⭐⭐⭐ — Drei Ziele.** Ergänzen Sie die Laufzeit als drittes Ziel (LKW schnell, Bahn langsam). Die Front wird zu einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand so schnell?
@@ -15953,7 +15953,7 @@ Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem in
## 14.12 Selbsttest {#sec:mehrziel-selbsttest}
1. Erklären Sie Pareto-Dominanz an einem Beispiel aus Ihrem Arbeitsumfeld.
-2. Warum ist „gewichtete Summe" bei einem reinen LP unproblematisch und bei einem MILP nicht?
+2. Warum ist „gewichtete Summe" bei einem reinen LP unproblematisch und bei einem [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} nicht?
3. Ein Kollege sagt: „Ich nehme $w = 0{,}5$, das ist neutral." Was ist daran falsch?
4. Beschreiben Sie das ε-Constraint-Verfahren in drei Sätzen, ohne Formeln.
5. Sie legen einer Geschäftsführung eine Front mit zwölf Punkten vor. Welche Spalte brauchen Sie, damit die Entscheidung fallen kann?
@@ -15978,9 +15978,9 @@ Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem in
>
> **Worum geht es?** Um die Naht zwischen zwei Welten, die in getrennten Abteilungen sitzen: Ein Modell **prognostiziert**, ein anderes **entscheidet**. Beide arbeiten sauber — und genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.
>
-> **Voraussetzungen:** [Kapitel 12](#kap-unsicherheit), insbesondere das Newsvendor-Problem`\index{Newsvendor-Problem}`{=latex} und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
+> **Voraussetzungen:** [Kapitel 12](#kap-unsicherheit), insbesondere das [Newsvendor-Problem](#gloss:newsvendor-problem){.glossar-link}`\index{Newsvendor-Problem}`{=latex} und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
>
-> **Danach können Sie:** begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige **Zielgröße** einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
+> **Danach können Sie:** begründen, warum ein Prognosemodell mit kleinerem [MSE](#gloss:mse){.glossar-link} teurere Entscheidungen erzeugen kann; die richtige **Zielgröße** einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.
>
> **Zeitbedarf:** ca. 3,5 Stunden.
>
@@ -16059,11 +16059,11 @@ In fast jedem Unternehmen sieht der Ablauf so aus:
Data Science Disposition
```
-Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein **Fehlermaß** — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
+Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein **Fehlermaß** — meist den mittleren quadratischen Fehler (MSE) oder [MAPE](#gloss:mape){.glossar-link} — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.
Der Fehler steckt nicht in einer der beiden Hälften, sondern in der Naht:
-> **📖 Definition: Predict-then-Optimize**`\index{Predict-then-Optimize}`{=latex}
+> **📖 Definition: [Predict-then-Optimize](#gloss:predict-then-optimize){.glossar-link}**`\index{Predict-then-Optimize}`{=latex}
>
> Das übliche zweistufige Vorgehen: erst eine unbekannte Größe schätzen, dann mit der Schätzung optimieren, als wäre sie die Wahrheit.
>
@@ -16494,7 +16494,7 @@ Bei einem halben Jahr Testdaten sieht das (bezogen auf den MSE) schlechtere Mode
Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert" — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: **das Prognosemodell direkt auf die Entscheidungskosten zu trainieren** statt auf ein Fehlermaß.
-Hier war es ein einziger Parameter`\index{Parameter (Modell)}`{=latex}, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man **durch das Optimierungsproblem hindurch ableiten**.
+Hier war es ein einziger [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex}, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man **durch das Optimierungsproblem hindurch ableiten**.
| Ansatz | Idee | Bibliothek |
| --- | --- | --- |
@@ -16530,7 +16530,7 @@ Hier war es ein einziger Parameter`\index{Parameter (Modell)}`{=latex}, mit eine
>
> Ein Data-Science-Team stellt sein Quartalsergebnis vor:
>
-> > *„Wir haben das alte lineare Modell durch ein Gradient`\index{Gradient}`{=latex}-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte."*
+> > *„Wir haben das alte lineare Modell durch ein [Gradient](#gloss:gradient){.glossar-link}`\index{Gradient}`{=latex}-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte."*
>
> Sechs Wochen später meldet die Disposition, die Retouren seien gestiegen.
>
@@ -16584,11 +16584,11 @@ Hier war es ein einziger Parameter`\index{Parameter (Modell)}`{=latex}, mit eine
| Was nicht gilt | Woran Sie es merken | Werkzeug | Kapitel |
| --- | --- | --- | --- |
-| **Linearität** | Risiko wächst quadratisch, Kosten degressiv, Sättigung | QP, konvexe Optimierung, KKT | [Kapitel 11](#kap-qp-nlp) |
+| **Linearität** | Risiko wächst quadratisch, Kosten degressiv, Sättigung | [QP](#gloss:quadratische-programmierung-qp){.glossar-link}, konvexe Optimierung, [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link} | [Kapitel 11](#kap-qp-nlp) |
| **Feste Daten** | Nachfrage, Wind, Rendite stehen erst morgen fest | Monte-Carlo, Zweistufigkeit, Robustheit, Chance Constraints | [Kapitel 12](#kap-unsicherheit) |
-| **Eine Periode** | die Entscheidung heute verändert, was übermorgen möglich ist | Bellman-Gleichung`\index{Bellman-Gleichung}`{=latex}, Rückwärtsinduktion`\index{Rückwärtsinduktion}`{=latex} | [Kapitel 13](#kap-dynamische-programmierung) |
-| **Ein Ziel** | zwei Kennzahlen sollen gleichzeitig stimmen | Pareto-Front`\index{Pareto-Front}`{=latex}, ε-Constraint | [Kapitel 14](#kap-mehrziel) |
-| **Daten sind gegeben** | die Eingabe ist selbst eine Prognose | Predict-then-Optimize`\index{Predict-then-Optimize}`{=latex}, entscheidungsorientiertes Lernen | [Kapitel 15](#kap-prognose) |
+| **Eine Periode** | die Entscheidung heute verändert, was übermorgen möglich ist | [Bellman-Gleichung](#gloss:bellman-gleichung){.glossar-link}`\index{Bellman-Gleichung}`{=latex}, [Rückwärtsinduktion](#gloss:rueckwaertsinduktion){.glossar-link}`\index{Rückwärtsinduktion}`{=latex} | [Kapitel 13](#kap-dynamische-programmierung) |
+| **Ein Ziel** | zwei Kennzahlen sollen gleichzeitig stimmen | [Pareto-Front](#gloss:pareto-front){.glossar-link}`\index{Pareto-Front}`{=latex}, ε-Constraint | [Kapitel 14](#kap-mehrziel) |
+| **Daten sind gegeben** | die Eingabe ist selbst eine Prognose | [Predict-then-Optimize](#gloss:predict-then-optimize){.glossar-link}`\index{Predict-then-Optimize}`{=latex}, entscheidungsorientiertes Lernen | [Kapitel 15](#kap-prognose) |
Die letzte Zeile ist die unbequemste: Dort ist nicht das Modell unsicher, sondern seine **Eingabe** — und die kommt aus einem zweiten Modell, das nach anderen Kriterien gebaut wurde.
@@ -16599,17 +16599,17 @@ Die letzte Zeile ist die unbequemste: Dort ist nicht das Modell unsicher, sonder
| „Mit dem Mittelwert zu rechnen ist eine brauchbare Näherung." | Der Mittelwertplan hält seine Zusage in **50,08 %** der Fälle — per Definition | [Abschnitt 12.7](#sec:unsicherheit-chance-constraints) |
| „Versorgungssicherheit kostet linear." | 56 289 € je Prozentpunkt auf dem Weg zu 80 %, **253 848 €** zwischen 95 und 99 % — das 4,5-fache | [Abschnitt 12.7](#sec:unsicherheit-chance-constraints) |
| „Eine Zusage aus dem Modell hält." | Der 95-%-Plan hält **87,44 %**, sobald die Testverteilung einen Fall enthält, in dem alles zugleich ausfällt | [Abschnitt 12.7](#sec:unsicherheit-chance-constraints) |
-| „Die bessere Prognose führt zur besseren Entscheidung." | Das Modell mit dem **schlechteren** MSE trifft die günstigere Entscheidung | [Kapitel 15](#kap-prognose) |
+| „Die bessere Prognose führt zur besseren Entscheidung." | Das Modell mit dem **schlechteren** [MSE](#gloss:mse){.glossar-link} trifft die günstigere Entscheidung | [Kapitel 15](#kap-prognose) |
## Drei Fehler, die dieser Teil verhindert
1. **Unsicherheit durch Mittelwerte ersetzen.** Der Durchschnittskunde kauft nie. Bei asymmetrischen Kosten liegt das Optimum systematisch neben dem Mittelwert, und eine Terminzusage ist ein **Quantil**, kein Erwartungswert.
-2. **Konvexität`\index{Konvexität}`{=latex} voraussetzen, ohne sie zu prüfen.** Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der Solver sagt das nicht von selbst.
+2. **[Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex} voraussetzen, ohne sie zu prüfen.** Ohne sie gibt es keine Optimalitätsgarantie, sondern nur ein lokales Ergebnis — und der [Solver](#gloss:solver){.glossar-link} sagt das nicht von selbst.
3. **Zwei Ziele mit einem Gewicht verrechnen, bevor die Front bekannt ist.** Ein Gewicht ist eine Antwort auf eine Frage, die noch niemand gestellt hat. Die Pareto-Front stellt sie zuerst.
## Wenn Sie nur eines mitnehmen
-> **🎯** Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet Zustandsraum`\index{Zustandsraum}`{=latex}, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, **welche** Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
+> **🎯** Jede der vier Annahmen lässt sich einzeln aufgeben — aber jede kostet etwas Bestimmtes: Linearität kostet die Optimalitätsgarantie, feste Daten kosten Rechenzeit oder Vorsicht, eine Periode kostet [Zustandsraum](#gloss:zustandsraum){.glossar-link}`\index{Zustandsraum}`{=latex}, ein Ziel kostet eine Entscheidung, die nicht der Modellierer treffen darf. Wer weiß, **welche** Annahme bei ihm fällt, weiß auch, welchen Preis er zahlt.
---
@@ -16628,7 +16628,7 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
>
> Die Finanzkapitel ab [Kapitel 18](#kap-finanzdaten) sind eine **Vertiefungsdomäne**, kein Pflichtstoff. Wer beruflich mit Logistik, Produktion oder Energie zu tun hat, liest die ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas fehlt — die Methoden sind dieselben.
>
-> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** Schätzfehler`\index{Schätzfehler}`{=latex} in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
+> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.
**Ein Hinweis vorweg:** Die Kapitel ab [Kapitel 18](#kap-finanzdaten) laden aktuelle Kursdaten über `yfinance` und brauchen dafür einen **Internetzugang**. Weil die Daten sich täglich ändern, sind die abgedruckten Zahlen dort *Beispielläufe*, keine reproduzierbaren Werte — anders als im übrigen Buch. Das Brückenkapitel kommt ohne Internet aus.
@@ -16640,7 +16640,7 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
>
> **Worum geht es?** Um den Nachweis, dass die Verfahren aus Teil II und III unverändert in der Finanzwelt funktionieren — und um die drei Stellen, an denen die Analogie endet.
>
-> **Voraussetzungen:** [Kapitel 1](#kap-einfuehrung) (Allokation als LP), [Kapitel 12](#kap-unsicherheit) (Szenarien) und [Abschnitt 22.6](#sec:praxisfallen-or-kern) (`or_kern.py`).
+> **Voraussetzungen:** [Kapitel 1](#kap-einfuehrung) (Allokation als [LP](#gloss:lineare-programmierung-lp){.glossar-link}), [Kapitel 12](#kap-unsicherheit) (Szenarien) und [Abschnitt 22.6](#sec:praxisfallen-or-kern) (`or_kern.py`).
>
> **Danach können Sie:** ein betriebswirtschaftliches Modell in die andere Domäne übersetzen, ohne den Modellcode anzufassen; Schattenpreise in beiden Welten lesen; und benennen, welche Annahmen dabei **nicht** mitwandern.
>
@@ -16698,11 +16698,11 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
**Und jetzt der Punkt.** Die Klasse heißt `Produktionsproblem`, und das Depot passt hinein, ohne dass eine Zeile geändert wurde. Kein Adapter, keine Unterklasse, keine Fallunterscheidung — die Struktur ist dieselbe, weil das Problem dasselbe ist: *knappe Größen auf konkurrierende Verwendungen verteilen.*
-Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. **60,26 € je Einheit Risikobudget** — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der **Preis des Risikos**. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis`\index{Schattenpreis}`{=latex} aus [Kapitel 5](#kap-lp).
+Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. **60,26 € je Einheit Risikobudget** — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der **Preis des Risikos**. Es ist nicht bloß eine Analogie, es ist derselbe [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} aus [Kapitel 5](#kap-lp).
> **🎯 Merksatz** Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei Datensätze. Ob das der Fall ist, erkennt man daran, dass derselbe Code ohne Änderung läuft — nicht daran, dass eine Tabelle es behauptet.
-**Warum funktioniert das?** Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor`\index{Kapazitätsvektor}`{=latex} und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
+**Warum funktioniert das?** Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen [Kapazitätsvektor](#gloss:kapazitaetsvektor){.glossar-link}`\index{Kapazitätsvektor}`{=latex} und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
---
@@ -16712,7 +16712,7 @@ Nach diesem Kapitel können Sie …
1. … ein Allokationsproblem aus der einen Domäne in die andere übersetzen, indem Sie die Daten austauschen statt des Codes.
2. … den Schattenpreis in beiden Welten deuten — als Wert einer Ressource und als Preis des Risikos.
-3. … erklären, warum die CVaR-Formulierung aus [Kapitel 20](#kap-cvar) auf Lieferverzüge genauso passt wie auf Kursverluste.
+3. … erklären, warum die [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link}-Formulierung aus [Kapitel 20](#kap-cvar) auf Lieferverzüge genauso passt wie auf Kursverluste.
4. … die drei Stellen benennen, an denen die Analogie **nicht** trägt — und begründen, warum das Übertragen von Methoden dort gefährlich wird.
---
@@ -17148,7 +17148,7 @@ steht im Programm genau einmal und wird zweimal aufgerufen.
> | --- | --- | --- |
> | $w$ | Anteil je Anlageklasse | Bestellanteil je Lieferant |
> | $\ell_s$ | Verlust in Szenario $s$, in Prozentpunkten | Verzug in Szenario $s$, in Tagen |
-> | $\gamma$ | der VaR: „so schlimm wird es an 95 % der Tage höchstens" | „so viele Tage Verzug höchstens bei 95 % der Bestellungen" |
+> | $\gamma$ | der [VaR](#gloss:value-at-risk-var){.glossar-link}: „so schlimm wird es an 95 % der Tage höchstens" | „so viele Tage Verzug höchstens bei 95 % der Bestellungen" |
> | $u_s$ | Überschuss über diese Schwelle | dasselbe |
> | Zielwert | CVaR: Mittelwert der schlechtesten 5 % | dasselbe |
>
@@ -17174,7 +17174,7 @@ Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Ding
| --- | --- | --- |
| **Woher die Zahlen kommen** | gemessen: drei Montagestunden sind drei Montagestunden | geschätzt: die erwartete Rendite hat einen Fehler, der größer sein kann als die Unterschiede zwischen den Anlagen ([Kapitel 19](#kap-markowitz)) |
| **Ob die Vergangenheit trägt** | Lieferzeiten haben physikalische Ursachen und ändern sich langsam | Kurse entstehen aus dem Verhalten von Marktteilnehmern, die auf Modelle reagieren — ein erkanntes Muster verschwindet oft deshalb, weil es erkannt wurde ([Kapitel 21](#kap-handelsmaschine)) |
-| **Ob Teilbarkeit erlaubt ist** | 37,4 % eines Lieferanten gibt es nicht: Verträge, Mindestabnahmen, Rüstzeiten → MILP | 37,4 % eines Fonds sind ein normaler Auftrag → LP/QP genügt |
+| **Ob Teilbarkeit erlaubt ist** | 37,4 % eines Lieferanten gibt es nicht: Verträge, Mindestabnahmen, Rüstzeiten → [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} | 37,4 % eines Fonds sind ein normaler Auftrag → LP/[QP](#gloss:quadratische-programmierung-qp){.glossar-link} genügt |
> **⚠️ Der Fehler, vor dem diese Tabelle schützt**
>
@@ -17194,7 +17194,7 @@ Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Ding
**Aufgabe 16.2 ⭐ — Den Schattenpreis lesen.** Das Risikobudget hat einen Schattenpreis von 60,26 €. Die Aufsicht bietet an, es gegen eine Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — und welche Einschränkung dieser Aussage kennen Sie aus [Abschnitt 5.9](#sec:lp-entartung)?
-**Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts.** Übertragen Sie die Ledoit-Wolf-Shrinkage`\index{Ledoit-Wolf-Shrinkage}`{=latex} aus [Kapitel 18](#kap-finanzdaten) auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
+**Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts.** Übertragen Sie die [Ledoit-Wolf-Shrinkage](#gloss:ledoit-wolf-shrinkage){.glossar-link}`\index{Ledoit-Wolf-Shrinkage}`{=latex} aus [Kapitel 18](#kap-finanzdaten) auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
**Aufgabe 16.4 ⭐⭐ — CVaR mit Ganzzahligkeit.** Der Einkauf darf höchstens **drei** Lieferanten beauftragen. Ergänzen Sie die entsprechende Kardinalitätsbedingung ([Kapitel 6](#kap-milp), Muster 5) und vergleichen Sie den CVaR mit dem teilbaren Fall. Was kostet die Ganzzahligkeit?
@@ -17208,7 +17208,7 @@ Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Ding
>
> Eine Logistikerin liest dieses Kapitel und überträgt es zurück. Ihre Aufgabe: Aus 40 möglichen Lieferanten sollen die besten ausgewählt und Bestellmengen zugeteilt werden. Sie geht vor wie im Portfoliomanagement:
>
-> > *„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio."*
+> > *„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio."*
>
> Das Vorgehen ist methodisch sauber, die Rechnung stimmt, und das Ergebnis sieht plausibel aus.
>
@@ -17315,7 +17315,7 @@ Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Ding
Die Merit-Order — Kraftwerke nach Grenzkosten sortieren und von unten auffüllen — ist damit nur für einen **einzelnen** Zeitpunkt richtig. Sobald ein Tag geplant wird, hängt jede Stunde an allen anderen: Ein Block, der um 8 Uhr angefahren wird, muss bis mindestens 16 Uhr laufen, und diese Entscheidung fällt am Vorabend.
-> **🎯 Merksatz** Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen **ein** Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.
+> **🎯 Merksatz** Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen **ein** Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} und keine Sortierung.
**Warum funktioniert das?** Der Umschlagpunkt ist eine einfache Rechnung: Die Anfahrkostendifferenz (38 500 €) geteilt durch die Grenzkostendifferenz je Stunde (83 €/MWh × 120 MW = 9 960 €/h) ergibt 3,9 Stunden. Bei zwei Kraftwerken kann man das im Kopf ausrechnen — bei fünf Blöcken und 24 Stunden nicht mehr.
@@ -17328,7 +17328,7 @@ Nach diesem Kapitel können Sie …
1. … ein Unit-Commitment-Modell mit Anfahrkosten und Mindestlaufzeiten aufstellen.
2. … erklären, warum eine **binäre erste Stufe** das zweistufige Modell aus [Kapitel 12](#kap-unsicherheit) verschärft.
3. … zeigen, dass eine Planung auf den Erwartungswert nicht nur ungenau, sondern **systematisch zu knapp** ist.
-4. … eine CVaR-Schranke auf eine physikalische Größe (nicht gedeckte Energie) legen.
+4. … eine [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link}-Schranke auf eine physikalische Größe (nicht gedeckte Energie) legen.
5. … begründen, wann eine Risikoschranke etwas ändert — und wann sie überflüssig ist.
---
@@ -17892,7 +17892,7 @@ Das ist keine akademische Feinheit, sondern die reale Lage der Energiewirtschaft
**Aufgabe 17.4 ⭐⭐ — Der Wert eines Speichers.** Ergänzen Sie einen Batteriespeicher (100 MW, 400 MWh, Wirkungsgrad 90 %). Um wie viel sinken die erwarteten Kosten — und was ist der Speicher damit je MWh Kapazität wert?
-**Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird.** Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap`\index{MIP-Gap}`{=latex} und Rechenzeit ([Abschnitt 6.8](#sec:milp-gap)). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus [Kapitel 9](#kap-metaheuristiken) würden Sie einsetzen?
+**Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird.** Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie [MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex} und Rechenzeit ([Abschnitt 6.8](#sec:milp-gap)). Ab wann wird das Problem für den exakten [Solver](#gloss:solver){.glossar-link} zu groß — und welches Verfahren aus [Kapitel 9](#kap-metaheuristiken) würden Sie einsetzen?
---
@@ -17954,7 +17954,7 @@ Das ist keine akademische Feinheit, sondern die reale Lage der Energiewirtschaft
>
> **Voraussetzungen:** [Kapitel 11](#kap-qp-nlp) (Eigenwerte, positive Definitheit), Grundlagen der Statistik.
>
-> **Danach können Sie:** Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, Ledoit-Wolf-Shrinkage`\index{Ledoit-Wolf-Shrinkage}`{=latex} anwenden — und erkennen, wann eine Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} aus zu wenigen Beobachtungen stammt.
+> **Danach können Sie:** Kursdaten korrekt laden, diskrete und logarithmische Renditen unterscheiden, das Schätzfehlerproblem erklären, [Ledoit-Wolf-Shrinkage](#gloss:ledoit-wolf-shrinkage){.glossar-link}`\index{Ledoit-Wolf-Shrinkage}`{=latex} anwenden — und erkennen, wann eine [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} aus zu wenigen Beobachtungen stammt.
>
> **Zeitbedarf:** ca. 5 Stunden.
>
@@ -18011,7 +18011,7 @@ Logarithmische Renditen dagegen **addieren** sich. Ihre Summe beträgt hier 18,2
| Verknüpfung über die Zeit | multiplikativ: $\prod(1+r_t)$ | **additiv**: $\sum \ln(1+r_t)$ |
| Mitteln zulässig? | nein — führt systematisch zu hoch | ja |
| Verknüpfung über Anlagen | **additiv**: $\sum w_i r_i$ | nein (Summe von Logs ≠ Log der Summe) |
-| Wofür also | Portfoliogewichtung, Kennzahlen je Periode | Zeitreihenanalyse, Volatilität`\index{Volatilität}`{=latex}, Modelle |
+| Wofür also | Portfoliogewichtung, Kennzahlen je Periode | Zeitreihenanalyse, [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex}, Modelle |
> **🎯 Merksatz** Beide Renditearten sind richtig — für **verschiedene Richtungen**. Diskrete Renditen addieren sich über die **Anlagen** eines Portfolios, logarithmische über die **Zeit**. Wer sie verwechselt, bekommt keine Fehlermeldung, sondern eine plausible falsche Zahl.
@@ -18024,9 +18024,9 @@ Logarithmische Renditen dagegen **addieren** sich. Ihre Summe beträgt hier 18,2
Nach diesem Kapitel können Sie …
1. … erklären, wann man diskrete und wann logarithmische Renditen verwendet.
-2. … beschreiben, warum die Stichproben-Kovarianzmatrix`\index{Stichproben-Kovarianzmatrix}`{=latex} bei vielen Titeln unbrauchbar wird.
-3. … den **Error-Maximizer-Effekt**`\index{Error-Maximizer-Effekt}`{=latex} an einem Experiment nachweisen.
-4. … Ledoit-Wolf-Shrinkage anwenden und ihre Wirkung an Eigenwerten und Konditionszahl`\index{Konditionszahl}`{=latex} messen.
+2. … beschreiben, warum die [Stichproben-Kovarianzmatrix](#gloss:stichproben-kovarianzmatrix){.glossar-link}`\index{Stichproben-Kovarianzmatrix}`{=latex} bei vielen Titeln unbrauchbar wird.
+3. … den **[Error-Maximizer-Effekt](#gloss:error-maximizer-effekt){.glossar-link}**`\index{Error-Maximizer-Effekt}`{=latex} an einem Experiment nachweisen.
+4. … Ledoit-Wolf-Shrinkage anwenden und ihre Wirkung an Eigenwerten und [Konditionszahl](#gloss:konditionszahl){.glossar-link}`\index{Konditionszahl}`{=latex} messen.
5. … Kursdaten so laden, dass die Spaltenreihenfolge garantiert stimmt.
6. … begründen, warum eine Schätzung aus $T < N$ Beobachtungen dem Optimierer risikofreie Richtungen vorgaukelt — und welche drei Gegenmittel es gibt.
7. … ein Portfolio **außerhalb** des Schätzzeitraums bewerten statt darin.
@@ -18159,7 +18159,7 @@ $$
\mathbf{S} = \frac{1}{T-1}\mathbf{X}^\top\mathbf{X}
$$
-Das Problem: $\mathbf{S}$ hat $\frac{N(N+1)}{2}$ zu schätzende Parameter`\index{Parameter (Modell)}`{=latex}, aber nur $T \cdot N$ Datenpunkte. Bei $N = 50$ Titeln sind das **1275 Parameter** — geschätzt aus einem Jahr Daten ($T = 252$).
+Das Problem: $\mathbf{S}$ hat $\frac{N(N+1)}{2}$ zu schätzende [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex}, aber nur $T \cdot N$ Datenpunkte. Bei $N = 50$ Titeln sind das **1275 Parameter** — geschätzt aus einem Jahr Daten ($T = 252$).

@@ -18169,7 +18169,7 @@ Das Problem: $\mathbf{S}$ hat $\frac{N(N+1)}{2}$ zu schätzende Parameter`\index
| $T \approx N$ | Extrem instabil; Eigenwerte streuen weit |
| $T < N$ | $\mathbf{S}$ ist **singulär** — nicht invertierbar, Rang $< N$ |
-Die **Zufallsmatrizentheorie**`\index{Zufallsmatrizentheorie}`{=latex} (*Random Matrix Theory*, Marchenko-Pastur-Gesetz`\index{Marchenko-Pastur-Gesetz}`{=latex}) zeigt: Die kleinsten Eigenwerte von $\mathbf{S}$ werden **systematisch unterschätzt**, die größten überschätzt. Und genau das ist fatal, denn:
+Die **[Zufallsmatrizentheorie](#gloss:zufallsmatrizentheorie){.glossar-link}**`\index{Zufallsmatrizentheorie}`{=latex} (*Random Matrix Theory*, [Marchenko-Pastur-Gesetz](#gloss:marchenko-pastur-gesetz){.glossar-link}`\index{Marchenko-Pastur-Gesetz}`{=latex}) zeigt: Die kleinsten Eigenwerte von $\mathbf{S}$ werden **systematisch unterschätzt**, die größten überschätzt. Und genau das ist fatal, denn:
> **🎯 Der Error-Maximizer-Effekt** Ein Risikominimierer sucht die Richtungen mit der **kleinsten** Varianz — also genau jene Eigenrichtungen, deren Eigenwerte am stärksten **nach unten verzerrt** sind. Die Optimierung greift damit zielsicher in das Schätzrauschen hinein. Markowitz-Optimierung ist deshalb kein Fehlerdämpfer, sondern ein **Fehlerverstärker**.
@@ -18294,7 +18294,7 @@ Bestmoegliche Volatilitaet bei perfektem Wissen: 11.84 % p.a.
--------------------------------------------------------------------------------------------
```
-**Diese Tabelle ist das Kernargument des ganzen Kapitels.** Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit **75,5 % mehr Risiko** als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das **geschätzte** Risiko — und die Differenz ist der Schätzfehler, in den er hineinoptimiert hat.
+**Diese Tabelle ist das Kernargument des ganzen Kapitels.** Lesen Sie die erste Zeile: Bei nur 60 Beobachtungen für 40 Titel trägt das „risikominimale“ Portfolio in Wahrheit **75,5 % mehr Risiko** als nötig. Der Optimierer hat nicht das Risiko minimiert, sondern das **geschätzte** Risiko — und die Differenz ist der [Schätzfehler](#gloss:schaetzfehler){.glossar-link}, in den er hineinoptimiert hat.
Drei Beobachtungen:
@@ -18520,7 +18520,7 @@ if __name__ == "__main__":
**Aufgabe 18.4 ⭐⭐ — Spaltenreihenfolge prüfen.** Laden Sie fünf Ticker in einer bewusst unsortierten Reihenfolge. Zeigen Sie, dass `raw["Close"].columns` abweicht, und dass `raw["Close"][tickers]` das behebt. Was passiert, wenn ein Ticker nicht existiert?
-**Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen.** Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von $\delta$ nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit `sklearn` anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines GMV-Portfolios (nach dem Muster von `Schaetzrauschen_Demo.py`), (c) der Stabilität der Gewichte über rollierende Fenster.
+**Aufgabe 18.5 ⭐⭐⭐ — Beide Shrinkage-Ziele vergleichen.** Implementieren Sie das Konstant-Korrelations-Ziel vollständig (inklusive eigener Berechnung von $\delta$ nach Ledoit/Wolf 2003 oder per Kreuzvalidierung). Vergleichen Sie mit `sklearn` anhand: (a) der Konditionszahl, (b) der Out-of-Sample-Volatilität eines [GMV](#gloss:global-minimum-variance-portfolio-gmv){.glossar-link}-Portfolios (nach dem Muster von `Schaetzrauschen_Demo.py`), (c) der Stabilität der Gewichte über rollierende Fenster.
**Aufgabe 18.6 ⭐⭐⭐ — Error-Maximizer selbst messen.** Erweitern Sie `Schaetzrauschen_Demo.py` um eine dritte Variante: die **Gleichgewichtung** ($w_i = 1/N$), die überhaupt keine Schätzung benötigt. (a) Ab welchem $T/N$ schlägt die Stichproben-Optimierung die Gleichgewichtung? (b) Ab welchem $T/N$ schlägt Ledoit-Wolf sie? (c) Was folgt daraus für die Praxis?
@@ -18769,9 +18769,9 @@ e 0: \mathbf{w}^ op\mathbf{S}\mathbf{w} = 0$ | „In genau diesen Richtungen mis
>
> **Worum geht es?** Um das Modell, das 1952 die quantitative Finanzwirtschaft begründete — und um die Restriktionen, ohne die es in der Praxis unbrauchbar bleibt.
>
-> **Voraussetzungen:** [Kapitel 11](#kap-qp-nlp) (QP, KKT), [Kapitel 18](#kap-finanzdaten) (Kovarianz, Shrinkage).
+> **Voraussetzungen:** [Kapitel 11](#kap-qp-nlp) ([QP](#gloss:quadratische-programmierung-qp){.glossar-link}, [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}), [Kapitel 18](#kap-finanzdaten) (Kovarianz, Shrinkage).
>
-> **Danach können Sie:** Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die Korn-Transformation`\index{Korn-Transformation}`{=latex} bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
+> **Danach können Sie:** Die Effizienzgrenze berechnen, das Maximum-Sharpe-Portfolio über die [Korn-Transformation](#gloss:korn-transformation){.glossar-link}`\index{Korn-Transformation}`{=latex} bestimmen, institutionelle Nebenbedingungen einbauen — und beurteilen, wann eine Renditeschätzung das Ergebnis wertlos macht.
>
> **Zeitbedarf:** ca. 6 Stunden.
>
@@ -18805,9 +18805,9 @@ e 0: \mathbf{w}^ op\mathbf{S}\mathbf{w} = 0$ | „In genau diesen Richtungen mis
> mu = np.array([0.03, 0.08, 0.055]) # erwartete Rendite
> sig = np.array([0.05, 0.18, 0.11]) # Schwankung
> R = np.array([[1.0, 0.1, 0.2], [0.1, 1.0, 0.5], [0.2, 0.5, 1.0]])
-> S = np.outer(sig, sig) * R # Kovarianzmatrix
+> S = np.outer(sig, sig) * R # [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}
>
-> w, ziel = cp.Variable(3, nonneg=True), cp.Parameter(nonneg=True)
+> w, ziel = cp.Variable(3, nonneg=True), cp.[Parameter](#gloss:parameter){.glossar-link}(nonneg=True)
> problem = cp.Problem(cp.Minimize(cp.quad_form(w, S)),
> [cp.sum(w) == 1, mu @ w >= ziel])
>
@@ -18836,13 +18836,13 @@ Lesen Sie die dritte Spalte: Sie zeigt, wie viel Rendite je Einheit Risiko herau
* Bei 3 % Zielrendite: 0,69 — das Portfolio ist sicher, aber lässt Ertrag liegen.
* Bei **4 %**: **0,75** — das beste Verhältnis.
-* Bei 8 %: 0,44 — das renditestärkste Portfolio ist das **schlechteste** je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine Diversifikation`\index{Diversifikation}`{=latex} mehr.
+* Bei 8 %: 0,44 — das renditestärkste Portfolio ist das **schlechteste** je Risikoeinheit. Und es besteht zu 100 % aus Aktien: Wer die höchste Rendite fordert, bekommt zwangsläufig gar keine [Diversifikation](#gloss:diversifikation){.glossar-link}`\index{Diversifikation}`{=latex} mehr.
Von 3 % auf 8 % Rendite ist ein Zuwachs um Faktor 2,7 — das Risiko steigt dabei um Faktor 3,7. Die letzten Prozentpunkte Rendite sind die teuersten.
> **🎯 Merksatz** Ein Optimierer beantwortet nicht die Frage „was soll ich tun?“, sondern „was ist erreichbar?“. Die Effizienzlinie ist eine **Speisekarte**, keine Empfehlung. Welche Zeile die richtige ist, hängt von der Risikotragfähigkeit ab — und das ist keine mathematische, sondern eine unternehmerische Frage.
-**Warum funktioniert das?** Weil `cp.Parameter` das Modell nur **einmal** aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter`\index{Parameter (Modell)}`{=latex} in CVXPY: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand ([Abschnitt 3.8](#sec:oekosystem-vektorisierung)). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
+**Warum funktioniert das?** Weil `cp.Parameter` das Modell nur **einmal** aufbaut und dann sechsmal mit verschiedenen Zielwerten löst. Genau dafür gibt es Parameter`\index{Parameter (Modell)}`{=latex} in [CVXPY](#gloss:cvxpy){.glossar-link}: Bei 200 Punkten auf der Linie spart das den 200-fachen Kompilierungsaufwand ([Abschnitt 3.8](#sec:oekosystem-vektorisierung)). Der Rest dieses Kapitels führt die Effizienzlinie sauber aus, zeigt, wie man das beste Verhältnis direkt berechnet statt es abzulesen — und warum das Ganze mit geschätzten Renditen gefährlicher ist, als es hier aussieht.
---
@@ -18990,7 +18990,7 @@ $$
>
> | $\lambda$ | Verhalten | Ergebnis |
> | --- | --- | --- |
-> | $0$ | Risiko zählt allein | **GMV** — Global Minimum Variance Portfolio |
+> | $0$ | Risiko zählt allein | **[GMV](#gloss:global-minimum-variance-portfolio-gmv){.glossar-link}** — Global Minimum Variance Portfolio |
> | mittel | Abwägung | ein Punkt auf der Effizienzgrenze |
> | $\to\infty$ | Rendite zählt allein | alles in den Titel mit höchstem $\mu$ |
>
@@ -19027,13 +19027,13 @@ Die **Effizienzgrenze**`\index{Effizienzgrenze}`{=latex} (*efficient frontier*)

-Das **Tangentialportfolio**`\index{Tangentialportfolio}`{=latex} maximiert die **Sharpe Ratio**`\index{Sharpe-Ratio}`{=latex}:
+Das **[Tangentialportfolio](#gloss:tangentialportfolio){.glossar-link}**`\index{Tangentialportfolio}`{=latex} maximiert die **[Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}**`\index{Sharpe-Ratio}`{=latex}:
$$
\text{SR}(\mathbf{w}) = \frac{\mathbf{w}^\top\boldsymbol{\mu} - r_f}{\sqrt{\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}}
$$
-> **📐 Formel-Lesehilfe** Zähler: **Überrendite** über den risikolosen Zins $r_f$. Nenner: die Volatilität`\index{Volatilität}`{=latex}.
+> **📐 Formel-Lesehilfe** Zähler: **Überrendite** über den risikolosen Zins $r_f$. Nenner: die [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex}.
>
> **Ohne Formel gesagt:** „Wie viel Mehrertrag bekomme ich je Einheit Risiko, die ich eingehe?“ Eine Sharpe Ratio von 0,8 heißt: Für jeden Prozentpunkt Volatilität gibt es 0,8 Prozentpunkte Überrendite.
@@ -19070,7 +19070,7 @@ In der Praxis gelten regulatorische und interne Regeln:
| **Sektorlimit** | $\sum_{i\in\text{Tech}} w_i \le 0{,}35$ | Branchenkonzentration begrenzen |
| **Mindestposition** | $w_i = 0$ oder $w_i \ge w_{\min}$ | Streuverluste vermeiden |
| **Kardinalität** | $\sum_i z_i \le K$, $w_i \le M z_i$ | Verwaltungsaufwand begrenzen |
-| **Turnover-Grenze** | $\|\mathbf{w}-\mathbf{w}_{\text{alt}}\|_1 \le \tau$ | Transaktionskosten`\index{Transaktionskosten}`{=latex} dämpfen |
+| **Turnover-Grenze** | $\|\mathbf{w}-\mathbf{w}_{\text{alt}}\|_1 \le \tau$ | [Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} dämpfen |
### Was diese Regeln kosten
@@ -19384,7 +19384,7 @@ if __name__ == "__main__":
> **⚠️ Zur Reproduzierbarkeit** Dieses Programm lädt Live-Daten. **Ihre Zahlen werden abweichen.** Prüfen Sie stattdessen die **Struktur** des Ergebnisses: Hält die Sektorgrenze? Summieren sich die Gewichte auf 100 %? Liegt die Sharpe Ratio des Max-Sharpe-Portfolios über der des GMV? Genau dafür sind die `assert`-Prüfungen da.
-> **🎯 Der Realitätscheck: Gleichgewichtung** Das Programm vergleicht immer mit dem **1/N-Portfolio**. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen Schätzfehler`\index{Schätzfehler}`{=latex} enthält. **Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert.** Diese Messlatte sollte in jedem Portfolioprojekt stehen.
+> **🎯 Der Realitätscheck: Gleichgewichtung** Das Programm vergleicht immer mit dem **1/N-Portfolio**. Das ist kein Scherz: DeMiguel, Garlappi und Uppal zeigten 2009 in einer viel beachteten Studie, dass die naive Gleichgewichtung viele optimierte Strategien out of sample schlägt — weil sie keinerlei Schätzung benötigt und damit auch keinen [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} enthält. **Wenn Ihre Optimierung 1/N nicht schlägt, ist sie ihren Aufwand nicht wert.** Diese Messlatte sollte in jedem Portfolioprojekt stehen.
---
@@ -19402,7 +19402,7 @@ if __name__ == "__main__":
**Aufgabe 19.5 ⭐⭐ — Den Sektorfehler nachstellen.** Bauen Sie den Fehler bewusst nach: Verwenden Sie `tech_indices = [0,1,2,3]` auf den alphabetisch sortierten Spalten. Vergleichen Sie das Ergebnis mit der korrekten Version. (a) Welche Titel werden tatsächlich beschränkt? (b) Wie stark unterscheiden sich die Portfoliogewichte? (c) Fällt der Fehler in den ausgegebenen Kennzahlen auf?
-**Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen.** Erweitern Sie das Modell um „höchstens $K = 5$ Titel“ mit Binärvariablen. CVXPY braucht dafür `cp.Variable(n, boolean=True)` und einen MIQP-fähigen Solver (z. B. `SCIP` über `cp.SCIP` oder `ECOS_BB`). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
+**Aufgabe 19.6 ⭐⭐⭐ — Kardinalität ergänzen.** Erweitern Sie das Modell um „höchstens $K = 5$ Titel“ mit Binärvariablen. CVXPY braucht dafür `cp.Variable(n, boolean=True)` und einen [MIQP](#gloss:miqp){.glossar-link}-fähigen [Solver](#gloss:solver){.glossar-link} (z. B. `SCIP` über `cp.SCIP` oder `ECOS_BB`). Vergleichen Sie Sharpe Ratio und Rechenzeit mit dem unbeschränkten Fall.
**Aufgabe 19.7 ⭐⭐⭐ — Out-of-Sample-Test.** Teilen Sie die Historie in zwei Hälften. Optimieren Sie auf der ersten, bewerten Sie auf der zweiten. Vergleichen Sie GMV, Max Sharpe und 1/N. (a) Welches Portfolio gewinnt out of sample? (b) Wie ändert sich das Bild mit Ledoit-Wolf gegenüber der Stichprobenkovarianz? (c) Welche Schlussfolgerung ziehen Sie für die Praxis?
@@ -19410,7 +19410,7 @@ if __name__ == "__main__":
## 19.8 Finde den Denkfehler {#sec:markowitz-denkfehler}
-[Kapitel 18](#kap-finanzdaten) hat gezeigt, was eine schlecht geschätzte **Kovarianzmatrix**`\index{Kovarianzmatrix}`{=latex} anrichtet. Bei den erwarteten **Renditen** ist es schlimmer — und weil sie in der Zielfunktion`\index{Zielfunktion}`{=latex} stehen statt in der Nebenbedingung, fällt es schwerer auf.
+[Kapitel 18](#kap-finanzdaten) hat gezeigt, was eine schlecht geschätzte **Kovarianzmatrix**`\index{Kovarianzmatrix}`{=latex} anrichtet. Bei den erwarteten **Renditen** ist es schlimmer — und weil sie in der [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} stehen statt in der Nebenbedingung, fällt es schwerer auf.
> **🐛 Finde den Denkfehler: Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio**
>
@@ -19664,9 +19664,9 @@ Drei praktische Konsequenzen:
>
> **Worum geht es?** Um zwei Schwächen des Markowitz-Modells: Es unterschätzt Extremverluste und ignoriert die Kosten des Umschichtens. Beide lassen sich mit konvexen Mitteln beheben.
>
-> **Voraussetzungen:** [Kapitel 19](#kap-markowitz), [Kapitel 5](#kap-lp) (LP-Formulierungen).
+> **Voraussetzungen:** [Kapitel 19](#kap-markowitz), [Kapitel 5](#kap-lp) ([LP](#gloss:lineare-programmierung-lp){.glossar-link}-Formulierungen).
>
-> **Danach können Sie:** VaR und CVaR unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die $L_1$-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
+> **Danach können Sie:** [VaR](#gloss:value-at-risk-var){.glossar-link} und [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} unterscheiden, den CVaR nach Rockafellar/Uryasev als lineares Programm formulieren, Turnover über die $L_1$-Norm bestrafen — und begründen, warum VaR-Kennzahlen nicht über Einheiten addiert werden dürfen.
>
> **Zeitbedarf:** ca. 5 Stunden.
>
@@ -19733,11 +19733,11 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum Marktrenditen keine Normalverteilung haben und was daraus folgt.
2. … VaR und CVaR definieren und begründen, warum nur der CVaR kohärent ist.
-3. … das Rockafellar-Uryasev-Theorem`\index{Rockafellar-Uryasev-Theorem}`{=latex} anwenden, um den CVaR linear zu formulieren.
-4. … Transaktionskosten über die $L_1$-Norm modellieren.
+3. … das [Rockafellar-Uryasev-Theorem](#gloss:rockafellar-uryasev-theorem){.glossar-link}`\index{Rockafellar-Uryasev-Theorem}`{=latex} anwenden, um den CVaR linear zu formulieren.
+4. … [Transaktionskosten](#gloss:transaktionskosten){.glossar-link} über die $L_1$-Norm modellieren.
5. … Einheiten konsistent halten und Nebenbedingungen vektorisieren.
6. … erklären, warum zwei Anlagen mit identischem VaR völlig verschiedene Extremverluste haben können.
-7. … die Folgen fehlender Subadditivität`\index{Subadditivität}`{=latex} für die Verteilung von Risikobudgets benennen.
+7. … die Folgen fehlender [Subadditivität](#gloss:subadditivitaet){.glossar-link}`\index{Subadditivität}`{=latex} für die Verteilung von Risikobudgets benennen.
---
@@ -19885,7 +19885,7 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
**Die 6-Sigma-Zeile setzt die Sache ins Verhältnis:** Unter Normalverteilung wäre ein solcher Tag ein Ereignis von einmal in **zwei Millionen Jahren**. Unter der t-Verteilung mit drei Freiheitsgraden — die realen Aktienrenditen deutlich näher kommt — passiert er etwa alle **zwei Jahre**. Der Faktor beträgt fast **eine Million**.
-Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen *Gewinn* von 2 €, im Portfolio einen *Verlust* von 98 €. Diversifikation`\index{Diversifikation}`{=latex} hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung **senkt** das Risiko, wie es sein muss.
+Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen *Gewinn* von 2 €, im Portfolio einen *Verlust* von 98 €. [Diversifikation](#gloss:diversifikation){.glossar-link}`\index{Diversifikation}`{=latex} hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung **senkt** das Risiko, wie es sein muss.

@@ -19905,7 +19905,7 @@ dem Fat-Tail-Modell dieses Kapitels ($t$-Verteilung mit drei Freiheitsgraden). D
**Value at Risk ($\text{VaR}_\alpha$):**`\index{Value at Risk (VaR)}`{=latex} Der Verlust, der mit Wahrscheinlichkeit $\alpha$ nicht überschritten wird.
-**Conditional Value at Risk ($\text{CVaR}_\alpha$, auch *Expected Shortfall*`\index{Expected Shortfall}`{=latex}):**`\index{Conditional Value at Risk (CVaR)}`{=latex} Der **durchschnittliche Verlust in den schlimmsten $(1-\alpha)$ Prozent** der Fälle.
+**Conditional Value at Risk ($\text{CVaR}_\alpha$, auch *[Expected Shortfall](#gloss:expected-shortfall){.glossar-link}*`\index{Expected Shortfall}`{=latex}):**`\index{Conditional Value at Risk (CVaR)}`{=latex} Der **durchschnittliche Verlust in den schlimmsten $(1-\alpha)$ Prozent** der Fälle.
> **📐 Der Unterschied in einem Satz** Der VaR sagt: *„In 95 % der Tage verlieren Sie höchstens 1,86 %.“* Der CVaR sagt: *„Und wenn es doch schiefgeht, verlieren Sie im Mittel 2,99 %.“*
>
@@ -19917,7 +19917,7 @@ dem Fat-Tail-Modell dieses Kapitels ($t$-Verteilung mit drei Freiheitsgraden). D
| --- | --- | --- |
| Berücksichtigt Verlusthöhe im Rand | ✗ nein | ✓ ja |
| **Subadditiv**`\index{Subadditivität}`{=latex} ($\rho(A+B) \le \rho(A)+\rho(B)$) | ✗ nein | ✓ ja |
-| Kohärentes Risikomaß`\index{Kohärentes Risikomaß}`{=latex} | ✗ nein | ✓ ja |
+| [Kohärentes Risikomaß](#gloss:kohaerentes-risikomass){.glossar-link}`\index{Kohärentes Risikomaß}`{=latex} | ✗ nein | ✓ ja |
| Konvex und optimierbar | ✗ nein | ✓ ja |
| Regulatorischer Standard | bis Basel II | **ab Basel III** |
@@ -19944,7 +19944,7 @@ $$
>
> **Ohne Formel gesagt:** „Wähle eine Schwelle $\gamma$. Zähle für jedes Szenario, wie weit der Verlust darüber hinausgeht. Der CVaR ist die Schwelle plus der gemittelte Überschuss — und zwar für diejenige Schwelle, bei der diese Summe minimal wird.“
>
-> **Warum $u_s$ automatisch das Maximum wird:** Die Zielfunktion`\index{Zielfunktion}`{=latex} minimiert die Summe der $u_s$. Jedes $u_s$ wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
+> **Warum $u_s$ automatisch das Maximum wird:** Die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} minimiert die Summe der $u_s$. Jedes $u_s$ wird also so klein wie möglich gedrückt — bis an die Grenze, die die beiden Ungleichungen erlauben. Das ist genau das Maximum der beiden Untergrenzen.
> **🔤 Formel-Übersetzer: jedes Zeichen der Zielfunktion**
>
@@ -19962,7 +19962,7 @@ $$
>
> **In einem Satz:** *Der CVaR ist der Mittelwert der schlimmsten $1-\alpha$ Prozent — der Bruch vor der Summe sorgt allein dafür, dass durch die richtige Anzahl geteilt wird.*
>
-> > **⚠️ Die teuerste Verwechslung des Kapitels** > $\alpha$ und $1-\alpha$ zu vertauschen führt zu **keiner Fehlermeldung**. Das Modell > rechnet weiter, nur eben über die falsche Menge`\index{Konvexität!Menge}`{=latex}. An 2 000 simulierten Tagesrenditen mit > Fat Tails`\index{Fat Tails}`{=latex} gemessen: > > | Rechnung | gemittelt über | CVaR | > | --- | --- | --- | > | richtig ($\alpha = 0{,}95$, Rand 5 %) | 100 von 2 000 Szenarien | **3,43 %** | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | **0,18 %** | > > Ein Faktor **19** — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > **Die Gegenprobe kostet eine Zeile:** Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt $\alpha$ nicht.
+> > **⚠️ Die teuerste Verwechslung des Kapitels** > $\alpha$ und $1-\alpha$ zu vertauschen führt zu **keiner Fehlermeldung**. Das Modell > rechnet weiter, nur eben über die falsche Menge`\index{Konvexität!Menge}`{=latex}. An 2 000 simulierten Tagesrenditen mit > [Fat Tails](#gloss:fat-tails){.glossar-link}`\index{Fat Tails}`{=latex} gemessen: > > | Rechnung | gemittelt über | CVaR | > | --- | --- | --- | > | richtig ($\alpha = 0{,}95$, Rand 5 %) | 100 von 2 000 Szenarien | **3,43 %** | > | vertauscht (Rand 95 %) | 1 900 von 2 000 Szenarien | **0,18 %** | > > Ein Faktor **19** — und die zweite Zahl ist kein schlecht geschätztes Randrisiko, sondern > überhaupt kein Randmaß mehr: Sie mittelt über fast alle Tage und schließt die schlimmen > gerade nicht ein. Der größte Einzelverlust der Stichprobe beträgt 21,4 %. > > **Die Gegenprobe kostet eine Zeile:** Ist der berechnete CVaR nicht deutlich größer als > der mittlere Verlust, stimmt $\alpha$ nicht.
---
@@ -19983,7 +19983,7 @@ $$
>
> **Warum $L_1$ und nicht $L_2$?** Die $L_1$-Norm ist konvex (also optimierbar) und erzeugt zusätzlich **dünn besetzte Änderungen**: Sie bevorzugt wenige große Umschichtungen gegenüber vielen kleinen. Das entspricht genau dem, was man in der Praxis will — nicht 50 Kleinstorders mit je 3 € Mindestgebühr.
-In CVXPY`\index{CVXPY}`{=latex} schreibt man einfach `cp.norm1(w - w_alt)`; intern wird das in lineare Hilfsvariablen zerlegt.
+In [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} schreibt man einfach `cp.norm1(w - w_alt)`; intern wird das in lineare Hilfsvariablen zerlegt.
---
@@ -19997,7 +19997,7 @@ In CVXPY`\index{CVXPY}`{=latex} schreibt man einfach `cp.norm1(w - w_alt)`; inte
> cvar = gamma + (1/(S*(1-alpha))) * cp.sum(u) # TAEGLICH
> objective = cp.Maximize(mu @ w - lambda_risk * cvar - trans_costs)
> ```
-> Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: `lambda_risk = 1.5` bedeutet dann effektiv eine Risikoaversion von $1{,}5/252 \approx 0{,}006$. Das Modell ist nicht falsch im Sinne von unlösbar — aber der Parameter`\index{Parameter (Modell)}`{=latex} bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
+> Der Risikoterm ist dadurch faktisch um Faktor 252 zu schwach gewichtet: `lambda_risk = 1.5` bedeutet dann effektiv eine Risikoaversion von $1{,}5/252 \approx 0{,}006$. Das Modell ist nicht falsch im Sinne von unlösbar — aber der [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} bedeutet nicht, was er zu bedeuten scheint, und lässt sich daher nicht sinnvoll einstellen.
>
> **Dieses Programm rechnet deshalb durchgehend auf Tagesbasis** und annualisiert erst in der Ausgabe.
@@ -20204,9 +20204,9 @@ if __name__ == "__main__":
> bedingungen.append(u[s] >= -R[s] @ w - gamma) # S einzelne Bedingungen
> ```
>
-> Beide beschreiben **dasselbe Modell**. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem Backtest mit 60 Rebalancings ([Kapitel 21](#kap-handelsmaschine)) summiert sich das erheblich.
+> Beide beschreiben **dasselbe Modell**. Der Unterschied liegt im Aufbau: CVXPY muss im zweiten Fall 500 einzelne Ausdrucksbäume erzeugen, prüfen und zusammensetzen. Bei einem [Backtest](#gloss:backtest){.glossar-link} mit 60 Rebalancings ([Kapitel 21](#kap-handelsmaschine)) summiert sich das erheblich.
>
-> **Die allgemeine Regel:** Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der Solver rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
+> **Die allgemeine Regel:** Wenn Sie in einer Modellierungssprache eine Python-Schleife über Datenzeilen schreiben, prüfen Sie, ob sich dasselbe als Matrixoperation ausdrücken lässt. Der [Solver](#gloss:solver){.glossar-link} rechnet ohnehin mit Matrizen — der Umweg über einzelne Ausdrücke kostet nur Aufbauzeit.
> **⚠️ Die Standardabweichung ist in CVXPY keine Wurzel**
>
@@ -20217,13 +20217,13 @@ if __name__ == "__main__":
> risiko = cp.norm2(L.T @ w) # = sqrt(w' sigma w)
> ```
>
-> und **nicht** als das Naheliegende, `cp.sqrt(cp.quad_form(w, sigma))`. Der naheliegende Ausdruck ist nicht DCP und wird von CVXPY mit einem `DCPError` abgelehnt: `cp.sqrt` ist konkav und verlangt deshalb ein **konkaves** Argument, `quad_form` ist aber konvex. Die Regel wird verletzt, obwohl die Funktion`\index{Konvexität!Funktion}`{=latex} als ganze mathematisch völlig harmlos ist — $\sqrt{w^\top \Sigma w}$ ist konvex, DCP kann es nur nicht *sehen*.
+> und **nicht** als das Naheliegende, `cp.sqrt(cp.quad_form(w, sigma))`. Der naheliegende Ausdruck ist nicht [DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link} und wird von CVXPY mit einem `DCPError` abgelehnt: `cp.sqrt` ist konkav und verlangt deshalb ein **konkaves** Argument, `quad_form` ist aber konvex. Die Regel wird verletzt, obwohl die Funktion`\index{Konvexität!Funktion}`{=latex} als ganze mathematisch völlig harmlos ist — $\sqrt{w^\top \Sigma w}$ ist konvex, DCP kann es nur nicht *sehen*.
>
> Wichtig ist die Fehlerdiagnose dahinter, weil sie leicht in die Irre geht: `cp.psd_wrap(sigma)` sieht nach der Lösung aus und ist keine. Es unterdrückt die **numerische** Beanstandung, dass $\Sigma$ nicht als positiv semidefinit erkannt wird — gegen die **strukturelle** Regelverletzung im Aufbau hilft es nicht.
>
> Der Ausweg ist immer derselbe: Zerlegen Sie $\Sigma = LL^\top$ (Cholesky). Dann ist $w^\top \Sigma w = \lVert L^\top w\rVert_2^2$, und die Standardabweichung wird zur 2-Norm eines **affinen** Ausdrucks — konvex, regelkonform und für den Solver sogar die bessere Formulierung, weil sie direkt ein Kegelproblem ist. Der winzige Diagonalzuschlag $10^{-12}$ fängt den Fall ab, dass $\Sigma$ numerisch nur halbdefinit ist (mehr Titel als Handelstage, duplizierte Spalten). Die vollständige Fehlertabelle steht in [Anhang C](#anhang-fehlerdiagnose).
-> **💡 Zur Annualisierung von VaR und CVaR** Ein häufiger Fehler skaliert den täglichen CVaR mit $\sqrt{252}$. Diese **Wurzel-Zeit-Regel**`\index{Wurzel-Zeit-Regel}`{=latex} gilt streng nur für **Standardabweichungen** unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
+> **💡 Zur Annualisierung von VaR und CVaR** Ein häufiger Fehler skaliert den täglichen CVaR mit $\sqrt{252}$. Diese **[Wurzel-Zeit-Regel](#gloss:wurzel-zeit-regel){.glossar-link}**`\index{Wurzel-Zeit-Regel}`{=latex} gilt streng nur für **Standardabweichungen** unabhängig identisch verteilter Größen ohne Drift. Der CVaR ist ein Erwartungswert über einen Verteilungsrand — für ihn ist die Regel eine grobe Näherung, die bei fetten Rändern und Autokorrelation systematisch danebenliegt.
>
> **Dieses Buch weist VaR und CVaR deshalb als Tageswerte aus** und benennt die Skalierungsproblematik ausdrücklich. Wer Mehrtageshorizonte braucht, simuliert sie (Monte-Carlo aus [Kapitel 12](#kap-unsicherheit)) statt zu skalieren.
@@ -20241,7 +20241,7 @@ if __name__ == "__main__":
**Aufgabe 20.4 ⭐⭐ — Einheiten prüfen.** Angenommen, ein Modell verrechnet Jahresrendite gegen Tages-CVaR (siehe die Warnung in [Abschnitt 20.6](#sec:cvar-implementierung-cvar-portfolio-mit-reibung)). Berechnen Sie: Welchem „effektiven“ täglichen $\lambda$ entspräche `lambda_risk = 1.5` dadurch? Welchen Wert müsste man stattdessen setzen, um dieselbe Wirkung wie `RISIKOAVERSION = 1.5` im konsistenten Tagesmodell zu erzielen?
-**Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren.** Variieren Sie `RISIKOAVERSION` von 0 bis 20 und tragen Sie Rendite, Volatilität`\index{Volatilität}`{=latex}, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
+**Aufgabe 20.5 ⭐⭐⭐ — Risikoaversion kalibrieren.** Variieren Sie `RISIKOAVERSION` von 0 bis 20 und tragen Sie Rendite, [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex}, CVaR und Turnover gegeneinander auf. (a) Wie sieht die „Effizienzgrenze“ im Rendite-CVaR-Raum aus? (b) Bei welchem Wert entspricht das CVaR-Portfolio ungefähr dem Varianz-Portfolio? (c) Welchen Wert würden Sie einem konservativen Stiftungsfonds empfehlen — und wie begründen Sie ihn ohne Fachjargon?
**Aufgabe 20.6 ⭐⭐⭐ — Turnover-Grenze statt Strafe.** Ersetzen Sie den Kostenterm durch eine **harte Grenze** $\lVert\mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1 \le \tau$ und variieren Sie $\tau \in \{0{,}05;\ 0{,}1;\ 0{,}25;\ 0{,}5;\ 1{,}0\}$. (a) Wie verändert sich die erreichbare Rendite? (b) Was ist der Vorteil einer Grenze gegenüber einer Strafe — und was der Nachteil? (c) Wann würden Sie was einsetzen?
@@ -20320,7 +20320,7 @@ if __name__ == "__main__":
* **Einheiten konsistent halten.** Tägliche Renditen zu täglichem Risiko; annualisiert wird erst in der Ausgabe.
* **Vektorisieren Sie Szenario-Bedingungen** — das spart bei wiederholten Läufen erheblich Zeit.
-**Ausblick.** [Kapitel 21](#kap-handelsmaschine) fügt alles zusammen: Datenpipeline, Signal, Optimierung, Rebalancing`\index{Rebalancing}`{=latex} und ein Walk-Forward-Backtest`\index{Walk-Forward-Backtest}`{=latex} ohne Lookahead-Bias.
+**Ausblick.** [Kapitel 21](#kap-handelsmaschine) fügt alles zusammen: Datenpipeline, Signal, Optimierung, [Rebalancing](#gloss:rebalancing){.glossar-link}`\index{Rebalancing}`{=latex} und ein [Walk-Forward-Backtest](#gloss:walk-forward-backtest){.glossar-link}`\index{Walk-Forward-Backtest}`{=latex} ohne [Lookahead-Bias](#gloss:lookahead-bias){.glossar-link}.
---
@@ -20329,11 +20329,11 @@ if __name__ == "__main__":
> **📌 Kapitel auf einen Blick**
>
-> **Worum geht es?** Um das Zusammenführen von allem: Datenpipeline, Signal, Optimierung, Rebalancing`\index{Rebalancing}`{=latex}, Kostenverbuchung und ein Backtest, der nicht lügt.
+> **Worum geht es?** Um das Zusammenführen von allem: Datenpipeline, Signal, Optimierung, [Rebalancing](#gloss:rebalancing){.glossar-link}`\index{Rebalancing}`{=latex}, Kostenverbuchung und ein [Backtest](#gloss:backtest){.glossar-link}, der nicht lügt.
>
> **Voraussetzungen:** [Kapitel 18](#kap-finanzdaten) bis [Kapitel 20](#kap-cvar).
>
-> **Danach können Sie:** Eine Walk-Forward-Backtest`\index{Walk-Forward-Backtest}`{=latex}-Architektur bauen, Lookahead-Bias`\index{Lookahead-Bias}`{=latex} vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
+> **Danach können Sie:** Eine [Walk-Forward-Backtest](#gloss:walk-forward-backtest){.glossar-link}`\index{Walk-Forward-Backtest}`{=latex}-Architektur bauen, [Lookahead-Bias](#gloss:lookahead-bias){.glossar-link}`\index{Lookahead-Bias}`{=latex} vermeiden, Ergebnisse gegen eine Benchmark bewerten und typische Backtest-Fallen erkennen.
>
> **Zeitbedarf:** ca. 6 Stunden.
>
@@ -20405,7 +20405,7 @@ Nach diesem Kapitel können Sie …
1. … die sechs Bausteine einer quantitativen Engine benennen und ihre Reihenfolge begründen.
2. … einen Walk-Forward-Backtest`\index{Walk-Forward-Backtest}`{=latex} ohne Lookahead-Bias`\index{Lookahead-Bias}`{=latex} implementieren.
3. … Rebalancing-Termine korrekt auf Handelstage abbilden.
-4. … Transaktionskosten`\index{Transaktionskosten}`{=latex} und Gewichtsdrift realistisch verbuchen.
+4. … [Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} und Gewichtsdrift realistisch verbuchen.
5. … Backtest-Ergebnisse kritisch prüfen und die typischen Selbsttäuschungen erkennen.
6. … den maximalen Rückschlag als eigenständige Kennzahl berichten und begründen, warum Rendite und Sharpe allein nichts über den Verlauf aussagen.
7. … die Zahl der ausprobierten Varianten protokollieren und die Signifikanzschwelle entsprechend korrigieren.
@@ -20926,7 +20926,7 @@ FEHLER 1+2+3 kombiniert 25.41 % +19.80 %
**Lesen Sie diese Tabelle sehr genau.** Die zugrunde liegenden Daten sind **reiner Zufall** — es gibt keinerlei Prognosekraft, kein Signal, keine Struktur. Trotzdem:
* **Lookahead** erzeugt eine scheinbare Überrendite von **15,4 Prozentpunkten pro Jahr**. Ein einziger falscher Index — `iloc[t+1]` statt `iloc[t]` — verwandelt Rauschen in eine scheinbar brillante Strategie.
-* **Survivorship-Bias**`\index{Survivorship-Bias}`{=latex} liefert **13,1 Prozentpunkte** allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
+* **[Survivorship-Bias](#gloss:survivorship-bias){.glossar-link}**`\index{Survivorship-Bias}`{=latex} liefert **13,1 Prozentpunkte** allein dadurch, dass man das Universum im Nachhinein auf die Gewinner beschränkt.
* Selbst die **ehrliche** Variante zeigt noch +1,1 Prozentpunkte — reines Rauschen, das aber ohne Weiteres als „leichte Outperformance“ verkauft werden könnte.
**Die Lehre:** Wenn Sie in einem Backtest eine Überrendite von 15 % sehen, ist die erste Frage nicht „Wie kann ich das nutzen?“, sondern **„Wo ist mein Fehler?“**. Und wenn Sie keinen finden, suchen Sie weiter — die Wahrscheinlichkeit, dass Sie tatsächlich eine Goldader entdeckt haben, ist deutlich kleiner als die, dass ein Index verrutscht ist.
@@ -20938,8 +20938,8 @@ Die fünf Fallen im Einzelnen:
| 1 | **Lookahead-Bias** | Entscheidungen nutzen Daten, die es noch nicht gab | Zeitindex prüfen; Selbsttest wie oben |
| 2 | **Survivorship-Bias**`\index{Survivorship-Bias}`{=latex} | Nur heute existierende Titel im Universum | Historische Indexzusammensetzung verwenden |
| 3 | **Kostenblindheit** | Gebühren, Spread, Slippage ignoriert | Kosten im Modell **und** in der Verbuchung |
-| 4 | **Overfitting**`\index{Overfitting}`{=latex} | Parameter auf die Vergangenheit getrimmt | Out-of-Sample-Zeitraum strikt zurückhalten |
-| 5 | **Data Snooping**`\index{Data Snooping}`{=latex} | Viele Varianten getestet, beste berichtet | Zahl der Versuche protokollieren; Deflated Sharpe Ratio`\index{Sharpe-Ratio}`{=latex} |
+| 4 | **[Overfitting](#gloss:overfitting){.glossar-link}**`\index{Overfitting}`{=latex} | [Parameter](#gloss:parameter){.glossar-link} auf die Vergangenheit getrimmt | Out-of-Sample-Zeitraum strikt zurückhalten |
+| 5 | **[Data Snooping](#gloss:data-snooping){.glossar-link}**`\index{Data Snooping}`{=latex} | Viele Varianten getestet, beste berichtet | Zahl der Versuche protokollieren; Deflated [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}`\index{Sharpe-Ratio}`{=latex} |
> **🎯 Die unbequeme Wahrheit** Falle 5 ist die gefährlichste, weil sie sich nicht im Code zeigt, sondern im **Arbeitsprozess**. Wer zwanzig Parameterkombinationen durchprobiert und die beste berichtet, hat keine Strategie gefunden, sondern eine Zufallsschwankung ausgewählt. Marcos López de Prado hat gezeigt, dass die meisten veröffentlichten Backtests genau daran scheitern. **Protokollieren Sie, wie viele Varianten Sie getestet haben** — und berichten Sie diese Zahl mit.
@@ -20954,7 +20954,7 @@ Ein guter Backtest ist eine **notwendige**, keine hinreichende Bedingung. Was zw
| **Papierhandel** | Funktioniert es auf Live-Daten ohne Geld? | 3–6 Monate, oft schlechter als der Backtest |
| **Kapazitätsanalyse** | Bewegt meine eigene Order den Markt? | Begrenzt das einsetzbare Volumen |
| **Regimewechsel-Test** | Hält es in Krisen? | Backtest über 2008, 2020 separat auswerten |
-| **Betriebsrisiken** | Was, wenn der Datenfeed ausfällt? | Fallback-Strategie`\index{Fallback-Strategie}`{=latex}, Monitoring |
+| **Betriebsrisiken** | Was, wenn der Datenfeed ausfällt? | [Fallback-Strategie](#gloss:fallback-strategie){.glossar-link}`\index{Fallback-Strategie}`{=latex}, Monitoring |
| **Regulatorik** | MiFID II, Best Execution, Dokumentation | Protokollpflichten |
> **⚠️ Der Realitätsabschlag** Als Faustregel gilt: **Die reale Performance liegt deutlich unter dem Backtest** — durch Slippage, verzögerte Ausführung, Datenrevisionen und schlicht dadurch, dass die Zukunft nicht die Vergangenheit ist. Wer einen Backtest mit Sharpe 1,5 hat, sollte real mit deutlich weniger rechnen. Eine Strategie, die im Backtest nur knapp die Benchmark schlägt, wird es real nicht tun.
@@ -21244,7 +21244,7 @@ Sechs Kapitel, zwei Branchen, die sich für unvergleichbar halten. Die Werkstatt
| Produkt, Maschinenstunde | Titel, Kapital | Entscheidungsvariable`\index{Entscheidungsvariable}`{=latex} |
| Deckungsbeitrag | erwartete Rendite | linearer Zielterm |
| Kapazität, Liefervertrag | Positionsgrenze, Sektorlimit | Nebenbedingung |
-| Rüstkosten, Anfahrkosten | Transaktionskosten`\index{Transaktionskosten}`{=latex} | Fixkosten mit Binärvariable`\index{Binärvariable}`{=latex} (B1) |
+| Rüstkosten, Anfahrkosten | [Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} | Fixkosten mit Binärvariable`\index{Binärvariable}`{=latex} (B1) |
| Lastabwurf, Konventionalstrafe | Tail-Verlust | Straf- bzw. Risikoterm |
| Wind- und Nachfrageszenarien | Renditeszenarien | Szenariomenge |
@@ -21256,12 +21256,12 @@ Sechs Kapitel, zwei Branchen, die sich für unvergleichbar halten. Die Werkstatt
| --- | --- | --- |
| „Planen mit der Prognose ist gut genug." | Der Erwartungswert-Plan kostet **149 % mehr** (874 870 € statt 351 356 €) und wirft in **28 von 40** Szenarien Last ab | [Kapitel 17](#kap-supplychain) |
| „Dann sparen wir eben beim Brennstoff." | Genau das tut er: 12 725 € gespart, **536 239 €** Lastabwurf bezahlt — das 42-fache. Die Anfahrkosten sind in beiden Plänen gleich | [Kapitel 17](#kap-supplychain) |
-| „Der VaR sagt, wie riskant es ist." | VaR **1,86 %**, CVaR **2,99 %** — und der schlechteste Tag der Stichprobe (**−23,0 %**) ändert am VaR **nichts** | [Kapitel 20](#kap-cvar) |
-| „Die Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} ist eine Eingabe wie jede andere." | Aus kurzen Reihen geschätzt ist sie schlecht konditioniert; die Optimierung setzt dann auf Schätzrauschen | [Kapitel 18](#kap-finanzdaten) |
+| „Der [VaR](#gloss:value-at-risk-var){.glossar-link} sagt, wie riskant es ist." | VaR **1,86 %**, [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} **2,99 %** — und der schlechteste Tag der Stichprobe (**−23,0 %**) ändert am VaR **nichts** | [Kapitel 20](#kap-cvar) |
+| „Die [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} ist eine Eingabe wie jede andere." | Aus kurzen Reihen geschätzt ist sie schlecht konditioniert; die Optimierung setzt dann auf Schätzrauschen | [Kapitel 18](#kap-finanzdaten) |
## Drei Fehler, die dieser Teil verhindert
-1. **Bei Markowitz einsteigen.** Wer [Kapitel 18](#kap-finanzdaten) überspringt, optimiert seine eigenen Schätzfehler`\index{Schätzfehler}`{=latex} und wundert sich über Gewichte, die niemand halten würde.
+1. **Bei Markowitz einsteigen.** Wer [Kapitel 18](#kap-finanzdaten) überspringt, optimiert seine eigenen [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} und wundert sich über Gewichte, die niemand halten würde.
2. **Den VaR für ein Risikomaß halten.** Er sagt, **ob** die Schwelle gerissen wird, nicht **wie schlimm** es dahinter aussieht — und er ist nicht kohärent. Optimiert wird der CVaR.
3. **Trägheit unterschätzen.** Im Kraftwerkspark ist nicht die Prognosegüte das Problem, sondern dass sich ein Block mit acht Stunden Mindestlaufzeit um 18 Uhr nicht mehr herbeirufen lässt. Dieselbe Trägheit heißt im Depot Transaktionskosten.
@@ -21308,7 +21308,7 @@ Die vorangegangenen vierzehn Kapitel enden fast alle mit derselben Empfehlung: *
>
> ```python
> def pruefe_loesung(x, A, b, kosten, zielwert, ganzzahlig=(), toleranz=1e-6):
-> """Prueft eine Loesung gegen die Anforderungen - OHNE den Solver zu fragen."""
+> """Prueft eine Loesung gegen die Anforderungen - OHNE den [Solver](#gloss:solver){.glossar-link} zu fragen."""
> beanstandungen = []
>
> verbrauch = A @ x # 1. Werden Grenzen eingehalten?
@@ -21349,13 +21349,13 @@ Nach diesem Kapitel können Sie …
1. … die fünf typischen Praxisfallen benennen und je ein Gegenmittel nennen.
2. … ein unlösbares Modell in einen „am wenigsten schlechten“ Plan verwandeln.
-3. … Solver-Ergebnisse erklärbar machen (*Explainable OR*`\index{Explainable OR}`{=latex}).
-4. … für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der Schattenpreis`\index{Schattenpreis}`{=latex} diese Frage nur für die *nächste* Einheit beantwortet.
+3. … Solver-Ergebnisse erklärbar machen (*[Explainable OR](#gloss:explainable-or){.glossar-link}*`\index{Explainable OR}`{=latex}).
+4. … für jede Nebenbedingung beziffern, was sie den Plan kostet — und begründen, warum der [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} diese Frage nur für die *nächste* Einheit beantwortet.
5. … Zeitlimits und Optimalitätslücken sinnvoll setzen.
6. … die Architektur einer produktionsreifen OR-Plattform skizzieren.
7. … eine **modellunabhängige Abnahmeprüfung** schreiben, die eine Lösung gegen die Anforderungen prüft statt gegen das Modell.
8. … Status, Gap und Zeitausschöpfung protokollieren und begründen, warum eine konstante Laufzeit bei Zeitlimit ein Warnsignal ist.
-9. … ein Modell in Domänenschicht, Modellbauer und Lösungs-DTO trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
+9. … ein Modell in Domänenschicht, Modellbauer und Lösungs-[DTO](#gloss:dto-data-transfer-object){.glossar-link} trennen — und begründen, warum sich das nicht wegen des Solverwechsels lohnt, sondern wegen der Prüfbarkeit.
10. … die Statuswerte verschiedener Solverbibliotheken auf eine gemeinsame Sprache abbilden.
---
@@ -21366,10 +21366,10 @@ Nach diesem Kapitel können Sie …
### Falle 1 — Infeasibility`\index{Infeasibility}`{=latex}
-Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht `INFEASIBLE`.
+Wenn [Parameter](#gloss:parameter){.glossar-link} unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht `INFEASIBLE`.
* **Falsch:** Das System stürzt ab oder zeigt „Fehler“. Der Disponent steht ohne Plan da.
-* **Richtig:** **Hierarchische Relaxation.**`\index{Hierarchische Relaxation}`{=latex} Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable`\index{Schlupfvariable}`{=latex}. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, **wo** es klemmt.
+* **Richtig:** **[Hierarchische Relaxation](#gloss:hierarchische-relaxation){.glossar-link}.**`\index{Hierarchische Relaxation}`{=latex} Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable`\index{Schlupfvariable}`{=latex}. So liefert der Solver immer einen Plan — und zeigt zugleich präzise, **wo** es klemmt.
```python
#!/usr/bin/env python3
@@ -21490,11 +21490,11 @@ if __name__ == "__main__":
`\index{Black-Box-Effekt}`{=latex}Ein Planer oder Trader lehnt ein System ab, wenn er die Zuweisungen nicht nachvollziehen kann. **Akzeptanz ist keine Nebensache, sondern Voraussetzung für den Betrieb.**
-Jede Entscheidung braucht einen **Constraint-Trace**`\index{Constraint-Trace}`{=latex}:
+Jede Entscheidung braucht einen **[Constraint-Trace](#gloss:constraint-trace){.glossar-link}**`\index{Constraint-Trace}`{=latex}:
> *„Warum bekommt Person A die Stunde und nicht Person B?“* → *„Weil B heute bereits zwei Vertretungsstunden hat und bei einer dritten die gesetzliche Ruhezeit von 11 Stunden unterschritten würde. A hat 80 Strafpunkte weniger.“*
-Bei Portfolioentscheidungen: **Kostenzerlegung**`\index{Kostenzerlegung}`{=latex} ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in [Kapitel 7](#kap-cpsat) und [Kapitel 20](#kap-cvar)).
+Bei Portfolioentscheidungen: **[Kostenzerlegung](#gloss:kostenzerlegung){.glossar-link}**`\index{Kostenzerlegung}`{=latex} ausweisen — Alpha-Ertrag, Risikoprämie, Transaktionskostenstrafe (siehe das Muster in [Kapitel 7](#kap-cpsat) und [Kapitel 20](#kap-cvar)).
> **🎯 Die Faustregel für Erklärbarkeit** Für jede Entscheidung sollten Sie drei Fragen beantworten können:
> 1. **Warum diese Lösung?** — Zielfunktionswert und seine Bestandteile.
@@ -21623,7 +21623,7 @@ Derselbe Unterschied kehrt auf Modellebene wieder, und dort lässt er sich in Eu
Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine Liquiditätskrise und keine Zinswende.
-* **Robuste Schätzung** statt roher Historie: Ledoit-Wolf-Shrinkage`\index{Ledoit-Wolf-Shrinkage}`{=latex} ([Kapitel 18](#kap-finanzdaten)), Szenarien ([Kapitel 12](#kap-unsicherheit)).
+* **Robuste Schätzung** statt roher Historie: [Ledoit-Wolf-Shrinkage](#gloss:ledoit-wolf-shrinkage){.glossar-link}`\index{Ledoit-Wolf-Shrinkage}`{=latex} ([Kapitel 18](#kap-finanzdaten)), Szenarien ([Kapitel 12](#kap-unsicherheit)).
* **Harte Obergrenzen** für Einzelpositionen und Sektoren — auch dann, wenn der Solver rechnerisch alles in einen Titel legen möchte. Die Grenze kostet Ertrag im Normalfall und rettet im Ernstfall.
* **Rollierende Neuschätzung** statt einmaliger Kalibrierung.
@@ -21633,9 +21633,9 @@ Ausführlich in [Kapitel 21](#kap-handelsmaschine) behandelt. Die Kurzfassung: B
### Falle 5 — Laufzeitexplosion`\index{Laufzeitexplosion (Fehlerbild)}`{=latex}
-Exakte Optimalität kann bei MILP oder CP-SAT`\index{CP-SAT}`{=latex} Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
+Exakte Optimalität kann bei [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} oder [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße.
-| Einsatzszenario | Empfohlenes Zeitlimit | Akzeptabler MIP-Gap`\index{MIP-Gap}`{=latex} |
+| Einsatzszenario | Empfohlenes Zeitlimit | Akzeptabler [MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex} |
| --- | --- | --- |
| Interaktive Oberfläche | 5–10 Sekunden | 2–5 % |
| Batch tagsüber | 1–5 Minuten | 1–2 % |
@@ -21650,11 +21650,11 @@ Exakte Optimalität kann bei MILP oder CP-SAT`\index{CP-SAT}`{=latex} Minuten bi
## 22.4 Constraint Attribution`\index{Constraint Attribution}`{=latex}: welche Bedingung kostet wie viel? {#sec:praxisfallen-attribution}
-Der Constraint-Trace beantwortet „warum **diese** Zuweisung?“. Der Deletion Filter`\index{Deletion Filter}`{=latex} in [Anhang C](#anhang-fehlerdiagnose) beantwortet „warum geht es **gar nicht**?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
+Der Constraint-Trace beantwortet „warum **diese** Zuweisung?“. Der [Deletion Filter](#gloss:deletion-filter){.glossar-link}`\index{Deletion Filter}`{=latex} in [Anhang C](#anhang-fehlerdiagnose) beantwortet „warum geht es **gar nicht**?“. Der häufigste Fall in der Praxis liegt dazwischen, und für ihn hat bisher niemand ein Werkzeug bekommen:
> *Das Modell rechnet. Der Plan ist zulässig. Und trotzdem ist er enttäuschend — nur weiß niemand, woran es liegt.*
-Die Antwort heißt **Constraint Attribution**`\index{Constraint Attribution}`{=latex}: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit **Kosten** statt Zulässigkeit als Kriterium.
+Die Antwort heißt **[Constraint Attribution](#gloss:constraint-attribution){.glossar-link}**`\index{Constraint Attribution}`{=latex}: Jede Bedingungsgruppe wird einzeln gelockert, und gemessen wird, was sich am Zielwert ändert. Das ist dasselbe Verfahren wie der Deletion Filter — nur mit **Kosten** statt Zulässigkeit als Kriterium.
Gerechnet wird auf derselben Fabrik wie die Konfliktsuche in [Anhang C](#anhang-fehlerdiagnose), aber **nach der Reparatur**: Dort war die Lackierkapazität von 150 Stunden der Kern des Widerspruchs ($2 \cdot 40 + 3 \cdot 30 = 170 > 150$). Eine zweite Schicht hat sie auf 210 gebracht, der Mindestumsatz wurde auf 10 000 € gesenkt. Jetzt ist das Modell lösbar — und die Frage lautet nicht mehr „warum nicht?“, sondern „was kostet uns was?“.
@@ -22072,7 +22072,7 @@ aus.
### Drei Befunde, und keiner steht im Solverergebnis
-**Erstens: bindend heißt nicht teuer.** Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von **0,00 €**. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den `Erklaerbarkeit.py` oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines LP.
+**Erstens: bindend heißt nicht teuer.** Fünf Bedingungen binden, aber „Lackierbudget Schicht 2“ tut es mit einem Schattenpreis von **0,00 €**. Sie berührt den Plan, ohne ihn zu verteuern — wer sie nachverhandelt, gewinnt nichts. Das ist derselbe Unterschied, den `Erklaerbarkeit.py` oben an einer einzelnen Zuweisung zeigt, hier im Dualwert eines [LP](#gloss:lineare-programmierung-lp){.glossar-link}.
**Zweitens: Der Schattenpreis ist eine Momentaufnahme.** Er gilt für die **nächste** Einheit. Ein Planer kauft aber keine Einheit, sondern eine Schicht. Für die Lackiererei verspricht die Hochrechnung $4{,}00 \cdot 30 = 120$ €; gemessen kommen **60 €**. Der Preis von 4,00 € gilt exakt bis **+15 Stunden**, danach begrenzt etwas anderes — **die zweite Hälfte der Sonderschicht wäre bezahlt und wirkungslos.**
@@ -22109,7 +22109,7 @@ In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein **
| --- | --- | --- |
| **Quellsysteme** | ERP, Personalplanung, Marktdaten-Feeds | REST, Datenbanken, Broker-APIs |
| **Datenqualität** | Validierung, Plausibilität, unveränderlicher Snapshot | Pydantic, Pandera, Great Expectations |
-| **Optimierungs-Worker** | Modell bauen, lösen, Timeout, Fallback | Celery/Redis, OR-Tools`\index{OR-Tools}`{=latex}, HiGHS, CVXPY |
+| **Optimierungs-Worker** | Modell bauen, lösen, Timeout, Fallback | Celery/Redis, [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex}, [HiGHS](#gloss:highs){.glossar-link}, [CVXPY](#gloss:cvxpy){.glossar-link} |
| **Disposition** | Ergebnis prüfen, ändern, freigeben | Web-UI, Dashboard, Freigabeworkflow |
| **Ausführung** | Kalender-Sync, Benachrichtigung, Order-Routing | Kalender-APIs, Messaging, Broker |
| **Betrieb** | Monitoring, Audit-Trail, Alarme | Logging, Metriken, Versionierung |
@@ -22118,10 +22118,10 @@ In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein **
**Fünf Prinzipien, die sich in der Praxis bewährt haben:**
-1. **Snapshot-Prinzip.**`\index{Snapshot-Prinzip}`{=latex} Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
-2. **Zustandslosigkeit.**`\index{Zustandslosigkeit (OR-Plattform)}`{=latex} Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
-3. **Mensch in der Schleife.**`\index{Mensch in der Schleife}`{=latex} Der Optimierer **schlägt vor**, ein Mensch **entscheidet** — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
-4. **Fallback-Strategie.**`\index{Fallback-Strategie}`{=latex} Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
+1. **[Snapshot-Prinzip](#gloss:snapshot-prinzip){.glossar-link}.**`\index{Snapshot-Prinzip}`{=latex} Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.
+2. **[Zustandslosigkeit](#gloss:zustandslosigkeit){.glossar-link}.**`\index{Zustandslosigkeit (OR-Plattform)}`{=latex} Der Worker hält keinen Zustand; Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial.
+3. **[Mensch in der Schleife](#gloss:mensch-in-der-schleife){.glossar-link}.**`\index{Mensch in der Schleife}`{=latex} Der Optimierer **schlägt vor**, ein Mensch **entscheidet** — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab.
+4. **[Fallback-Strategie](#gloss:fallback-strategie){.glossar-link}.**`\index{Fallback-Strategie}`{=latex} Was passiert, wenn der Solver kein Ergebnis liefert? Antwort: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Nie: nichts.
5. **Versionierung.** Modellversion, Parametersatz und Solver-Version gehören ins Protokoll jedes Laufs. Ohne das lässt sich nicht klären, warum das Ergebnis von letzter Woche anders aussah.
> **⚠️ Der häufigste Projektfehler** Nicht das Modell scheitert, sondern die **Einführung**. Ein technisch überlegener Plan, den die Disponentin nicht versteht und dem sie nicht traut, wird umgangen — sie plant weiter in ihrer Tabelle. Rechnen Sie mindestens so viel Zeit für Erklärbarkeit, Schulung und schrittweise Einführung ein wie für das Modell selbst.
@@ -22162,7 +22162,7 @@ zeigt nach innen auf die Domäne, und die Domäne zeigt auf nichts. Erzeugt von
Der unscheinbarste, aber wirksamste Teil ist die Statusübersetzung. Dieselbe Aussage heißt in den fünf im Buch verwendeten Bibliotheken:
-| Bedeutung | pywraplp | CP-SAT | HiGHS`\index{HiGHS}`{=latex} | SciPy | CVXPY`\index{CVXPY}`{=latex} |
+| Bedeutung | pywraplp | [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} | HiGHS`\index{HiGHS}`{=latex} | [SciPy](#gloss:scipy){.glossar-link} | CVXPY`\index{CVXPY}`{=latex} |
| --- | --- | --- | --- | --- | --- |
| beweisbar optimal | `Solver.OPTIMAL` | `cp_model.OPTIMAL` | `"Optimal"` | `status == 0` | `"optimal"` |
| zulässig, nicht bewiesen | `Solver.FEASIBLE` | `cp_model.FEASIBLE` | `"Time limit reached"` | — | `"optimal_inaccurate"` |
@@ -22725,7 +22725,7 @@ Domaenenschicht.
> | `ressourcen` als sortierte Liste | feste Spaltenreihenfolge | Die Vertauschungsfalle aus [Kapitel 18](#kap-finanzdaten) wird hier strukturell unmöglich. |
> | `loese_mit_glop` / `loese_mit_scipy` | zwei Modellbauer, ein Auswertungscode | Der Solverwechsel betrifft **eine** Funktion. Alles danach — Bericht, Prüfung, Test — bleibt unverändert. |
> | die drei Fehlerfälle am Ende | Kapazität 0, unbekannte Ressource, doppelter Name | Alle drei scheitern beim **Einlesen**. Ohne Domänenschicht fielen sie erst beim Lösen auf — oder gar nicht. |
-> | `import pandas` **in** der Funktion | Excel-Schicht als optionale Abhängigkeit | Wer `or_kern` nur für Statusauswertung und Prüfung benutzt, soll pandas nicht installieren müssen. |
+> | `import pandas` **in** der Funktion | Excel-Schicht als optionale Abhängigkeit | Wer `or_kern` nur für Statusauswertung und Prüfung benutzt, soll [pandas](#gloss:pandas){.glossar-link} nicht installieren müssen. |
> | `schattenpreise` im DTO | Dualwerte gehören zur Lösung, nicht zum Solver | Beide Modellbauer liefern hier identische Werte (40 / 20) — der Begriff ist solverunabhängig, die Abfrage nicht. |
> **🎯 Merksatz** Ein Fehler, der beim Einlesen auffliegt, kostet Minuten. Derselbe Fehler, der erst im Bericht auffällt, kostet Tage — und derselbe Fehler, der **nie** auffällt, kostet am meisten. Validierung ist deshalb keine Fleißarbeit, sondern die günstigste Stelle im ganzen Ablauf.
@@ -22736,7 +22736,7 @@ Domaenenschicht.
Der vorige Abschnitt endet mit einer Behauptung: Ein Solverwechsel betreffe genau **einen** Baustein. Das ist die Art Satz, die in jedem Architekturvortrag vorkommt und die man deshalb nicht glauben sollte, bevor sie jemand vorgeführt hat.
-Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: **CP-SAT gegen HiGHS**. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer Branch-and-Bound`\index{Branch-and-Bound}`{=latex}-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren ([Kapitel 3](#kap-oekosystem)).
+Also führen wir sie vor — und zwar am unbequemsten Paar, das dieses Buch zu bieten hat: **CP-SAT gegen HiGHS**. Die beiden sind grundverschieden. CP-SAT ist ein Constraint-Programming-Solver, der ausschließlich ganzzahlig rechnet und Restriktionen als Ausdrücke entgegennimmt. HiGHS ist ein klassischer [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}`\index{Branch-and-Bound}`{=latex}-Löser, der eine Koeffizientenmatrix erwartet. Und sie lassen sich nicht einmal gemeinsam importieren ([Kapitel 3](#kap-oekosystem)).
**Die Aufgabe** ist eine kapazitierte Standortplanung: Aus sechs möglichen Lagern sind diejenigen zu eröffnen, mit denen zwölf Kunden am günstigsten beliefert werden. Jeder Kunde wird von genau einem Lager bedient, jedes eröffnete Lager kostet eine Fixgebühr, und die Kapazität reicht je Lager für 174 der insgesamt 387 Paletten — es müssen also mindestens drei Lager öffnen.
@@ -23161,7 +23161,7 @@ Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
- [ ] Für jede harte Bedingung ist geprüft: Muss sie wirklich hart`\index{Nebenbedingung!hart}`{=latex} sein?
- [ ] Es gibt eine Relaxationsstrategie für den Infeasible-Fall.
- [ ] Big-M-Werte sind so klein wie möglich gewählt.
-- [ ] Die Zielfunktion`\index{Zielfunktion}`{=latex} ist in konsistenten Einheiten formuliert.
+- [ ] Die [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} ist in konsistenten Einheiten formuliert.
**Daten**
- [ ] Spaltenreihenfolge und Datentypen werden nach jedem Import geprüft.
@@ -23187,16 +23187,16 @@ Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:
**Grundlagen und lineare/ganzzahlige Optimierung**
* Bertsimas & Tsitsiklis: *Introduction to Linear Optimization* — Standardwerk zu Simplex, Dualität, Polyedertheorie.
-* Wolsey: *Integer Programming* — Branch-and-Cut`\index{Branch-and-Cut}`{=latex}, Schnittebenen, Formulierungsstärke.
+* Wolsey: *Integer Programming* — [Branch-and-Cut](#gloss:branch-and-cut){.glossar-link}`\index{Branch-and-Cut}`{=latex}, Schnittebenen, Formulierungsstärke.
**Konvexe Optimierung**
* Boyd & Vandenberghe: *Convex Optimization* — Pflichtlektüre; frei verfügbar. Die theoretische Basis von CVXPY.
-**Constraint Programming und Scheduling**
+**Constraint Programming und [Scheduling](#gloss:scheduling){.glossar-link}**
* Google OR-Tools Dokumentation und das CP-SAT-Primer von Laurent Perron.
**Quantitative Finanzmathematik**
-* López de Prado: *Advances in Financial Machine Learning* — Backtest-Overfitting`\index{Overfitting}`{=latex}, Denoising, Deflated Sharpe Ratio`\index{Sharpe-Ratio}`{=latex}.
+* López de Prado: *Advances in Financial Machine Learning* — [Backtest](#gloss:backtest){.glossar-link}-[Overfitting](#gloss:overfitting){.glossar-link}`\index{Overfitting}`{=latex}, Denoising, Deflated [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}`\index{Sharpe-Ratio}`{=latex}.
* Cornuéjols, Peña & Tütüncü: *Optimization Methods in Finance*.
**Wie es weitergeht**
@@ -23207,11 +23207,11 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
| Richtung | Wofür | Einstieg |
| --- | --- | --- |
-| **Column Generation / Branch-and-Price**`\index{Column Generation}`{=latex} | sehr große Zuschnitt- und Dienstplanprobleme | [Kapitel 10](#kap-dekomposition), Wolsey |
-| **Benders-Zerlegung**`\index{Benders-Zerlegung}`{=latex} | zweistufige stochastische Modelle mit vielen Szenarien | Birge & Louveaux |
-| **Approximate Dynamic Programming / RL**`\index{Approximate Dynamic Programming}`{=latex} | hochdimensionale Zustandsräume | Powell: *Reinforcement Learning`\index{Reinforcement Learning}`{=latex} and Stochastic Optimization* |
-| **Konische Optimierung (SOCP, SDP)**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} | robuste Portfolios, Ellipsoid-Unsicherheit | Boyd & Vandenberghe, [Kapitel 5](#kap-lp) |
-| **Multi-Objective Optimization**`\index{Multi-Objective Optimization}`{=latex} | echte Zielkonflikte ohne Gewichtung | Ehrgott: *Multicriteria Optimization* |
+| **[Column Generation](#gloss:column-generation){.glossar-link} / Branch-and-Price**`\index{Column Generation}`{=latex} | sehr große Zuschnitt- und Dienstplanprobleme | [Kapitel 10](#kap-dekomposition), Wolsey |
+| **[Benders-Zerlegung](#gloss:benders-zerlegung){.glossar-link}**`\index{Benders-Zerlegung}`{=latex} | zweistufige stochastische Modelle mit vielen Szenarien | Birge & Louveaux |
+| **[Approximate Dynamic Programming](#gloss:approximate-dynamic-programming){.glossar-link} / RL**`\index{Approximate Dynamic Programming}`{=latex} | hochdimensionale Zustandsräume | Powell: *[Reinforcement Learning](#gloss:reinforcement-learning){.glossar-link}`\index{Reinforcement Learning}`{=latex} and Stochastic Optimization* |
+| **[Konische Optimierung](#gloss:konische-optimierung){.glossar-link} (SOCP, SDP)**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} | robuste Portfolios, Ellipsoid-Unsicherheit | Boyd & Vandenberghe, [Kapitel 5](#kap-lp) |
+| **[Multi-Objective Optimization](#gloss:multi-objective-optimization){.glossar-link}**`\index{Multi-Objective Optimization}`{=latex} | echte Zielkonflikte ohne Gewichtung | Ehrgott: *Multicriteria Optimization* |
---
@@ -23221,9 +23221,9 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
**Aufgabe 22.1 ⭐ — Hart oder weich`\index{Nebenbedingung!weich}`{=latex}, revisited.** Nennen Sie für ein Vertretungsplanungssystem je zwei Bedingungen, die (a) zwingend hart bleiben müssen, (b) unbedingt weich sein sollten, (c) diskutabel sind. Begründen Sie.
-**Aufgabe 22.2 ⭐ — Zeitlimit wählen.** Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-Rebalancing`\index{Rebalancing}`{=latex} monatlich.
+**Aufgabe 22.2 ⭐ — Zeitlimit wählen.** Für welche Szenarien setzen Sie welches Zeitlimit und welchen Gap? (a) Taxi-Disposition in Echtzeit, (b) Wochendienstplan, freitags erstellt, (c) Jahresproduktionsplanung, (d) Portfolio-[Rebalancing](#gloss:rebalancing){.glossar-link}`\index{Rebalancing}`{=latex} monatlich.
-**Aufgabe 22.3 ⭐⭐ — Relaxation`\index{Relaxation}`{=latex} einbauen.** Nehmen Sie Ihr Modell aus der Aufgabe *Eigener Dienstplan* ([Abschnitt 7.9](#sec:cpsat-uebungsaufgaben)) und machen Sie es `INFEASIBLE`-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
+**Aufgabe 22.3 ⭐⭐ — [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} einbauen.** Nehmen Sie Ihr Modell aus der Aufgabe *Eigener Dienstplan* ([Abschnitt 7.9](#sec:cpsat-uebungsaufgaben)) und machen Sie es `INFEASIBLE`-sicher: Schlupfvariablen für unbesetzte Schichten, gestaffelte Strafen. Testen Sie mit einem bewusst überlasteten Szenario.
**Aufgabe 22.4 ⭐⭐ — Erklärbarkeit implementieren.** Erweitern Sie ein beliebiges Modell aus dem Kurs um einen `erklaere(loesung)`-Report, der die drei Fragen aus [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen) beantwortet.
@@ -23231,13 +23231,13 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
**Aufgabe 22.6 ⭐⭐⭐ — Gap gegen Laufzeit.** Nehmen Sie das MILP-Portfolio aus [Kapitel 6](#kap-milp) und vergrößern Sie es auf 50 Anlagen. Messen Sie Laufzeit und Zielwert bei Gap-Vorgaben von 10 %, 5 %, 2 %, 1 %, 0,1 % und 0 %. Stellen Sie den Zusammenhang grafisch dar und leiten Sie eine Empfehlung ab.
-**Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben.** Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle`\index{Schattenpreis!Vorzeichenfalle}`{=latex} in [Abschnitt 5.7](#sec:lp-die-vorzeichenfalle-bei-schattenpreisen), die ungültige Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} in [Kapitel 11](#kap-qp-nlp), die Sektor-Positionsindizes in [Kapitel 19](#kap-markowitz), die vermischten Einheiten in [Abschnitt 20.6](#sec:cvar-implementierung-cvar-portfolio-mit-reibung) oder die Rebalancing-Termine in [Abschnitt 21.4](#sec:handelsmaschine-rebalancing-termine-richtig-bestimmen)) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
+**Aufgabe 22.7 ⭐⭐⭐ — Post-Mortem schreiben.** Suchen Sie sich einen der im Buch besprochenen Fehler aus (z. B. die Vorzeichenfalle`\index{Schattenpreis!Vorzeichenfalle}`{=latex} in [Abschnitt 5.7](#sec:lp-die-vorzeichenfalle-bei-schattenpreisen), die ungültige [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} in [Kapitel 11](#kap-qp-nlp), die Sektor-Positionsindizes in [Kapitel 19](#kap-markowitz), die vermischten Einheiten in [Abschnitt 20.6](#sec:cvar-implementierung-cvar-portfolio-mit-reibung) oder die Rebalancing-Termine in [Abschnitt 21.4](#sec:handelsmaschine-rebalancing-termine-richtig-bestimmen)) und schreiben Sie ein einseitiges Post-Mortem: Was war der Fehler? Warum fiel er nicht auf? Welche Prüfung hätte ihn gefunden? Welche Regel leiten Sie daraus für eigene Projekte ab?
---
## 22.11 Finde den Denkfehler {#sec:praxisfallen-denkfehler}
-Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als **gar nichts**.
+Falle 5 — die [Laufzeitexplosion](#gloss:laufzeitexplosion){.glossar-link} — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als **gar nichts**.
> **🐛 Finde den Denkfehler: Das Modell, das seit einem Jahr nicht mehr optimiert**
>
@@ -23521,7 +23521,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
>
> **2. Eine Prüffunktion soll kontrollieren, ob ein Tourenplan die Fahrzeugkapazitäten einhält. Woher nimmt sie die geladenen Mengen?** (a) Aus der Ladungsdimension des Routing-Modells — dort stehen sie ja bereits. (b) Sie summiert die Bedarfe der Kunden aus dem **ausgegebenen Tourenplan** auf. Alles andere fragt das Modell, ob es sich an sich selbst hält. (c) Aus der Zielfunktion, sofern die Kapazität dort bepreist ist.
>
-> **3. Ein Modell meldet `INFEASIBLE`, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt?** (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren Strafkosten`\index{Strafkosten}`{=latex} statt Verboten —, damit der Solver benennt, **welche** Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
+> **3. Ein Modell meldet `INFEASIBLE`, nachdem eine neue Regel aufgenommen wurde. Was ist der produktivste erste Schritt?** (a) Das Zeitlimit erhöhen. (b) Die neue Regel wieder entfernen und in Ruhe abwarten. (c) Die harten Regeln hierarchisch lockern — mit teuren [Strafkosten](#gloss:strafkosten){.glossar-link}`\index{Strafkosten}`{=latex} statt Verboten —, damit der Solver benennt, **welche** Regel den Widerspruch erzeugt, statt nur zu sagen, dass es einen gibt.
---
@@ -23541,7 +23541,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
## 22.14 Zusammenfassung {#sec:praxisfallen-zusammenfassung}
-* **Infeasibility ist ein Entwurfsproblem, kein Solverproblem.** Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
+* **[Infeasibility](#gloss:infeasibility){.glossar-link} ist ein Entwurfsproblem, kein Solverproblem.** Wer alles hart formuliert, erhält irgendwann eine Fehlermeldung statt eines Plans.
* **Erklärbarkeit entscheidet über Akzeptanz.** Schattenpreise, Kostenzerlegung und Constraint-Traces sind keine Kür.
* **Bindend heißt nicht teuer.** Eine Bedingung kann den Plan berühren und trotzdem 0,00 € kosten. Nur die kostenrelevanten lohnen eine Nachverhandlung.
* **Der Schattenpreis gilt für die nächste Einheit, nicht für die nächste Schicht.** Hochgerechnet auf einen realistischen Schritt überschätzt er den Nutzen — im Beispiel um das Doppelte. Wer wissen will, was ein Hebel bringt, **misst** die Lockerung, statt zu multiplizieren; die beiden Ranglisten können sich dabei umdrehen.
@@ -23584,11 +23584,11 @@ sichtbar wird, und gibt Zeit zum Handeln.
> **🚀 In 5 Minuten gelöst: drei Zeilen, die einen echten Fehler fangen**
>
-> Die Schreinerei aus [Kapitel 1](#kap-einfuehrung), nur mit etwas knapperen Kapazitäten. Der Solver liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
+> Die Schreinerei aus [Kapitel 1](#kap-einfuehrung), nur mit etwas knapperen Kapazitäten. Der [Solver](#gloss:solver){.glossar-link} liefert einen Plan, halbe Tische gibt es nicht — also wird gerundet.
>
> ```python
> import numpy as np
-> from scipy.optimize import linprog
+> from scipy.optimize import [linprog](#gloss:linprog){.glossar-link}
>
> verbrauch_je_stueck = np.array([[3.0, 1.0], # Montagestunden je Tisch/Stuhl
> [6.0, 1.0]]) # Plattenmaterial je Tisch/Stuhl
@@ -23615,7 +23615,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
> AssertionError: Plan [31. 48.] braucht [141. 234.], verfuegbar sind [140. 232.]
> ```
-**Und jetzt der Punkt.** Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des LP. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
+**Und jetzt der Punkt.** Der Solver hat richtig gerechnet: 30,67 Tische und 48 Stühle sind das Optimum des [LP](#gloss:lineare-programmierung-lp){.glossar-link}. Das Runden hat den Plan kaputt gemacht — 31 Tische brauchen eine Montagestunde mehr, als es gibt. Ohne die drei Zeilen wäre dieser Plan in die Fertigung gegangen, und dort hätte am Monatsende eine Stunde gefehlt, die niemand einer Zeile Python zugeordnet hätte.
Bemerkenswert ist, **was** der Test prüft. Er prüft nicht, ob 30,67 das Optimum ist — das könnte er gar nicht, dafür bräuchte er einen zweiten Solver. Er prüft die Eigenschaft, die jede brauchbare Lösung haben muss: *Sie hält die Kapazität ein.* Diese Verschiebung — **von der Frage nach dem richtigen Wert zur Frage nach den notwendigen Eigenschaften** — ist der Schlüssel zum ganzen Kapitel.
@@ -23984,7 +23984,7 @@ if __name__ == "__main__":
>
> | Stelle | Was passiert |
> | --- | --- |
-> | `@pytest.fixture(params=MODELLBAUER, ids=NAMEN)` | Jeder Test läuft **zweimal** — einmal mit GLOP, einmal mit SciPy. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell |
+> | `@pytest.fixture(params=MODELLBAUER, ids=NAMEN)` | Jeder Test läuft **zweimal** — einmal mit [GLOP](#gloss:glop){.glossar-link}, einmal mit [SciPy](#gloss:scipy){.glossar-link}. Ein Test, der nur einen Solver sieht, prüft die Bibliothek mit statt das Modell |
> | `test_beide_solver_liefern_dasselbe` | Die Architektur aus [Kapitel 22](#kap-praxisfallen) als Test: gleicher Zielwert **und** gleiche Schattenpreise |
> | `test_waehrungseinheit_skaliert_linear` | prüft nicht Mathematik, sondern **Numerik** — schlecht skalierte Modelle ([Kapitel 2](#kap-fundament)) fallen hier durch |
> | `test_produktreihenfolge_aendert_nichts` | die Spaltenvertauschungsfalle aus [Kapitel 18](#kap-finanzdaten), als Invariante |
@@ -24556,13 +24556,13 @@ Sie sagt, wie viel der Zeit in den **Aufbau** des Modells geht statt ins Lösen
*(Die genauen Prozentwerte schwanken von Lauf zu Lauf — es sind Verhältnisse zweier Zeitmessungen. Die Größenordnungen sind stabil.)*
-Bei OR-Tools gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren *Solver* sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau ([Kapitel 3](#kap-oekosystem)).
+Bei [OR-Tools](#gloss:or-tools){.glossar-link} gehen drei von vier Sekunden dafür drauf, in Python 10 000 Variablenobjekte anzulegen und Nebenbedingungen daraus zusammenzusetzen. Wer an dieser Stelle einen schnelleren *Solver* sucht, sucht in der falschen Hälfte — die Antwort ist ein vektorisierter Modellaufbau ([Kapitel 3](#kap-oekosystem)).
-Die zweite unbequeme Zahl steht ganz rechts: **CVXPY braucht rund 240 MB**, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
+Die zweite unbequeme Zahl steht ganz rechts: **[CVXPY](#gloss:cvxpy){.glossar-link} braucht rund 240 MB**, unabhängig von der Problemgröße — das ist der Preis seiner Modellierungsschicht. Auf einem Rechner mit knappem Speicher entscheidet diese Spalte und nicht die Laufzeit darüber, was überhaupt läuft.
> **⚠️ Was diese Tabelle nicht sagt**
>
-> Sie sagt **nicht** „ortools ist langsamer als scipy". Gemessen wurde *ein* Problemtyp in *einer* Formulierung auf *einer* Maschine. Ein MILP statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
+> Sie sagt **nicht** „ortools ist langsamer als scipy". Gemessen wurde *ein* Problemtyp in *einer* Formulierung auf *einer* Maschine. Ein [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} statt eines LP, eine andere Modellierung desselben Problems oder ein anderer Rechner können die Reihenfolge umdrehen — und bei MILPs tun sie es regelmäßig.
>
> Übertragbar ist der **Trend**, nicht der Absolutwert: dass der Aufbauanteil mit der Größe wächst, dass CVXPY einen konstanten Speichersockel hat. Der Zweck des Programms ist, dass Sie es auf Ihrer Maschine mit Ihrem Problem laufen lassen.
@@ -24985,15 +24985,15 @@ Das zugehoerige Dockerfile ist kurz genug, um es ganz zu zeigen:
> **💻 Code-Durchgang: drei Entscheidungen**
>
-> **1. Das Domänenmodell ist das API-Schema.** Der Parameter heißt `problem: Produktionsproblem` — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus [Kapitel 1](#kap-einfuehrung) und antwortet bei Unsinn mit `422`, **bevor** ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die `or_kern.py` schon beim Einlesen abfängt.
+> **1. Das Domänenmodell ist das API-Schema.** Der [Parameter](#gloss:parameter){.glossar-link} heißt `problem: Produktionsproblem` — mehr steht nicht da. FastAPI validiert die Anfrage damit gegen dieselben Pydantic-Regeln wie der Excel-Import aus [Kapitel 1](#kap-einfuehrung) und antwortet bei Unsinn mit `422`, **bevor** ein Solver startet. Die drei abgewiesenen Anfragen im Selbsttest sind genau die drei Fälle, die `or_kern.py` schon beim Einlesen abfängt.
>
-> **2. Die Antwort enthält Status und Gap, nicht nur Zahlen.** Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das `Loesung`-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe DTO, das in [Abschnitt 22.7](#sec:praxisfallen-solverwechsel) schon eine Prozessgrenze überlebt hat.
+> **2. Die Antwort enthält Status und Gap, nicht nur Zahlen.** Ein Aufrufer, der nur die Mengen bekommt, kann nicht unterscheiden, ob er ein bewiesenes Optimum oder einen Zeitlimit-Abbruch in der Hand hält. Das `Loesung`-Objekt geht deshalb vollständig durch die Schnittstelle — dasselbe [DTO](#gloss:dto-data-transfer-object){.glossar-link}, das in [Abschnitt 22.7](#sec:praxisfallen-solverwechsel) schon eine Prozessgrenze überlebt hat.
>
> **3. Die Abnahmeprüfung läuft auch hier.** `pruefe_loesung()` steht im Arbeiter, nicht nur im Skript. Ein Dienst ohne sie liefert Fehler schneller aus, als ein Mensch sie finden könnte.
### Threads oder Prozesse? Eine Messung, keine Meinung
-Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: **OR-Tools rechnet in C++ und gibt den GIL während `Solve()` frei.** Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
+Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: **OR-Tools rechnet in C++ und gibt den [GIL](#gloss:gil-global-interpreter-lock){.glossar-link} während `Solve()` frei.** Ein Threadpool genügt also, es braucht keine eigenen Prozesse.
Das gilt aber nicht allgemein. Eine in reinem Python geschriebene Heuristik ([Kapitel 9](#kap-metaheuristiken)) hält den GIL die ganze Zeit — für sie braucht derselbe Dienst einen `ProcessPoolExecutor`. Die Entscheidung gehört gemessen, nicht angenommen.
@@ -25093,11 +25093,11 @@ Zwei Kapitel, ein Übergang: Das Modell rechnet — und muss jetzt jemand andere
| Was in Produktion passiert | Gegenmittel | Wo |
| --- | --- | --- |
-| `INFEASIBLE` um 3 Uhr nachts, niemand weiß warum | Relaxation`\index{Relaxation}`{=latex} mit gestaffelten Strafkosten`\index{Strafkosten}`{=latex} (B18); Deletion Filter`\index{Deletion Filter}`{=latex} für die Diagnose | [Kapitel 22](#kap-praxisfallen), [Anhang C](#anhang-fehlerdiagnose) |
-| Der Disponent lehnt den Plan ab, weil er ihn nicht versteht | Constraint-Trace`\index{Constraint-Trace}`{=latex} für die einzelne Zuweisung; Kostenzurechnung für den ganzen Plan | [Kapitel 22](#kap-praxisfallen), [Abschnitt 22.4](#sec:praxisfallen-attribution) |
-| Der Solver läuft ins Zeitlimit und keiner merkt es | Status, Gap und Zeitausschöpfung protokollieren — konstante Laufzeit ist ein Warnsignal | [Kapitel 22](#kap-praxisfallen) |
+| `INFEASIBLE` um 3 Uhr nachts, niemand weiß warum | [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} mit gestaffelten [Strafkosten](#gloss:strafkosten){.glossar-link}`\index{Strafkosten}`{=latex} (B18); [Deletion Filter](#gloss:deletion-filter){.glossar-link}`\index{Deletion Filter}`{=latex} für die Diagnose | [Kapitel 22](#kap-praxisfallen), [Anhang C](#anhang-fehlerdiagnose) |
+| Der Disponent lehnt den Plan ab, weil er ihn nicht versteht | [Constraint-Trace](#gloss:constraint-trace){.glossar-link}`\index{Constraint-Trace}`{=latex} für die einzelne Zuweisung; Kostenzurechnung für den ganzen Plan | [Kapitel 22](#kap-praxisfallen), [Abschnitt 22.4](#sec:praxisfallen-attribution) |
+| Der [Solver](#gloss:solver){.glossar-link} läuft ins Zeitlimit und keiner merkt es | Status, Gap und Zeitausschöpfung protokollieren — konstante Laufzeit ist ein Warnsignal | [Kapitel 22](#kap-praxisfallen) |
| Ein Test wird grundlos mal rot, mal grün | auf Zielwert und Regeln prüfen, nicht auf die Gestalt der Lösung; `num_workers = 1` **und** Seed | [Abschnitt 7.7](#sec:cpsat-parallele-suche), [Kapitel 23](#kap-testing) |
-| Ein Ergebnis lässt sich später nicht mehr nachvollziehen | Snapshot-Prinzip`\index{Snapshot-Prinzip}`{=latex}: unveränderlicher Datenstand mit ID je Lauf | [Kapitel 22](#kap-praxisfallen) |
+| Ein Ergebnis lässt sich später nicht mehr nachvollziehen | [Snapshot-Prinzip](#gloss:snapshot-prinzip){.glossar-link}`\index{Snapshot-Prinzip}`{=latex}: unveränderlicher Datenstand mit ID je Lauf | [Kapitel 22](#kap-praxisfallen) |
| Das Modell prüft sich selbst | Abnahmeprüfung als eigener Baustein, ohne Solver und ohne Modellvariable | [Abschnitt 22.6](#sec:praxisfallen-or-kern) |
## Was dieser Teil gemessen hat
@@ -25106,7 +25106,7 @@ Zwei Kapitel, ein Übergang: Das Modell rechnet — und muss jetzt jemand andere
| --- | --- | --- |
| „Der Solver nennt schon den Grund für `INFEASIBLE`." | Er nennt gar nichts. Der Deletion Filter findet einen kleinsten Konflikt — dieses Modell enthält **neun** verschiedene, und welchen man sieht, steuert die Prüfreihenfolge | [Anhang C](#anhang-fehlerdiagnose) |
| „Bindende Bedingungen sind die teuren." | Von fünf bindenden Bedingungen kostet eine **0,00 €** | [Abschnitt 22.4](#sec:praxisfallen-attribution) |
-| „Der Schattenpreis`\index{Schattenpreis}`{=latex} sagt, was der Hebel bringt." | Hochgerechnet 120 €, gemessen **60 €** — er gilt nur bis +15 Stunden. Die Ranglisten nach Preis und nach Wirkung drehen sich um | [Abschnitt 22.4](#sec:praxisfallen-attribution) |
+| „Der [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} sagt, was der Hebel bringt." | Hochgerechnet 120 €, gemessen **60 €** — er gilt nur bis +15 Stunden. Die Ranglisten nach Preis und nach Wirkung drehen sich um | [Abschnitt 22.4](#sec:praxisfallen-attribution) |
| „Mehr Arbeiter sind proportional schneller." | **12,3-fach** bei acht Arbeitern — überlinear, weil verschiedene Strategien statt derselben Suche laufen | [Abschnitt 7.7](#sec:cpsat-parallele-suche) |
## Drei Fehler, die dieser Teil verhindert
@@ -25165,11 +25165,11 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
* Variablen: $x_{p,s} \in \{0,1\}$ — Person $p$ übernimmt Slot $s$.
* Hart: genau eine Person je Slot; Qualifikation; keine Doppelbelegung; Höchstzahl Vertretungsstunden; Ruhezeiten.
* Weich: Vorbelastung, Freistundenlöcher, Fachnähe, Wünsche, Fairness über die Woche.
-* Solver: CP-SAT`\index{CP-SAT}`{=latex}.
+* [Solver](#gloss:solver){.glossar-link}: [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}.
**Abnahmekriterien.**
- [ ] Läuft in unter 10 Sekunden für eine ganze Schule (60 Lehrkräfte, 30 Slots).
-- [ ] Liefert **immer** einen Plan — auch wenn nicht alle Slots besetzbar sind (Relaxation`\index{Relaxation}`{=latex}).
+- [ ] Liefert **immer** einen Plan — auch wenn nicht alle Slots besetzbar sind ([Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex}).
- [ ] Weist je Zuweisung eine Begründung aus.
- [ ] Ausgabe als CSV **und** als lesbare Tagestabelle.
- [ ] Vergleich gegen die manuelle Lösung: Wie viele Wünsche werden erfüllt?
@@ -25193,15 +25193,15 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
* Variablen: $x_{p,t,s} \in \{0,1\}$ — Person, Tag, Schicht.
* Hart: Mindestbesetzung je Schicht **und** Qualifikationsmix (mindestens eine examinierte Kraft); nach Nachtschicht ≥ 2 freie Tage; höchstens 6 Arbeitstage in Folge; monatliche Sollstunden ± 10 %.
* Weich: Wunschfrei, gleichmäßige Wochenendverteilung, ungeteilte Dienste, stabile Schichtfolgen (nicht Früh–Nacht–Früh).
-* Solver: CP-SAT mit Intervall- und Cumulative-Constraints.
+* Solver: [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} mit Intervall- und Cumulative-Constraints.
**Abnahmekriterien.**
- [ ] Alle gesetzlichen Regeln nachweislich eingehalten (per `assert` geprüft).
-- [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt (Gini-Koeffizient`\index{Gini-Koeffizient}`{=latex}).
+- [ ] Wunscherfüllungsquote wird ausgewiesen und ist fair verteilt ([Gini-Koeffizient](#gloss:gini-koeffizient){.glossar-link}`\index{Gini-Koeffizient}`{=latex}).
- [ ] Läuft in unter 60 Sekunden.
- [ ] Bei Unlösbarkeit: Notfallplan plus benannte Ursache.
-**Stolperfallen.** Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — Symmetriebrechung`\index{Symmetriebrechung}`{=latex} und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
+**Stolperfallen.** Der 28-Tage-Zyklus erzeugt viele Variablen (25 × 28 × 3 = 2100) — [Symmetriebrechung](#gloss:symmetriebrechung){.glossar-link}`\index{Symmetriebrechung}`{=latex} und gute Suchheuristiken werden wichtig. Fairness über einen Monat ist etwas anderes als Fairness über eine Woche.
---
@@ -25213,7 +25213,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
**Daten.** Adressen aus einer CSV; Entfernungen über OpenStreetMap (`osmnx`, `openrouteservice`) oder als Luftlinie mit Umwegfaktor 1,3 als Näherung.
-**Modellskizze.** OR-Tools`\index{OR-Tools}`{=latex} Routing-Bibliothek`\index{Routing-Bibliothek}`{=latex} mit Kapazitäts- und Zeitdimension; Metaheuristik`\index{Metaheuristik}`{=latex} `GUIDED_LOCAL_SEARCH`, Zeitlimit 30 Sekunden.
+**Modellskizze.** [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} [Routing-Bibliothek](#gloss:routing-bibliothek){.glossar-link}`\index{Routing-Bibliothek}`{=latex} mit Kapazitäts- und Zeitdimension; [Metaheuristik](#gloss:metaheuristik){.glossar-link}`\index{Metaheuristik}`{=latex} `GUIDED_LOCAL_SEARCH`, Zeitlimit 30 Sekunden.
**Abnahmekriterien.**
- [ ] Alle Kunden werden innerhalb ihrer Zeitfenster beliefert.
@@ -25238,7 +25238,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
* Ziel: Fixkosten + Transportkosten minimieren.
**Abnahmekriterien.**
-- [ ] Sensitivitätsanalyse`\index{Sensitivitätsanalyse}`{=latex}: Wie verändert sich die Lösung bei ±20 % Transportkosten?
+- [ ] [Sensitivitätsanalyse](#gloss:sensitivitaetsanalyse){.glossar-link}`\index{Sensitivitätsanalyse}`{=latex}: Wie verändert sich die Lösung bei ±20 % Transportkosten?
- [ ] Was-wäre-wenn: Standort X wird politisch vorgegeben — was kostet das?
- [ ] Kartendarstellung mit Zuordnungslinien.
- [ ] Amortisationsrechnung über 10 Jahre.
@@ -25251,7 +25251,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
> **Schwierigkeit:** ⭐⭐⭐ · **Kapitel:** [Kapitel 5](#kap-lp), [Kapitel 6](#kap-milp), [Kapitel 7](#kap-cpsat) · **Zeit:** ca. 18 Stunden
-**Ausgangslage.** Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet Rüstzeit`\index{Rüstzeit}`{=latex}; die Rüstzeit hängt von der Reihenfolge ab.
+**Ausgangslage.** Eine Fertigung produziert 15 Varianten auf 4 Maschinen. Jeder Produktwechsel kostet [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex}; die Rüstzeit hängt von der Reihenfolge ab.
**Modellskizze.** Los- und Reihenfolgeplanung:
* Variablen: Produktionsmengen, Rüstentscheidungen, Reihenfolge (Intervallvariablen).
@@ -25276,14 +25276,14 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
**Modellskizze.**
* Variablen: Zielgewichte $w_i$; Binärvariablen für „Position wird gehandelt“.
-* Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, [Kapitel 6](#kap-milp)), höchstens $K$ Transaktionen je Rebalancing`\index{Rebalancing}`{=latex}.
-* Ziel: erwartete Rendite − Risikoterm − Transaktionskosten`\index{Transaktionskosten}`{=latex} − Steuerwirkung.
-* Solver: CVXPY`\index{CVXPY}`{=latex} (konvexer Teil) oder MIQP für die Kardinalität.
+* Hart: Vollinvestition, keine Leerverkäufe, Positionsobergrenzen, Mindestordergröße (semikontinuierlich, [Kapitel 6](#kap-milp)), höchstens $K$ Transaktionen je [Rebalancing](#gloss:rebalancing){.glossar-link}`\index{Rebalancing}`{=latex}.
+* Ziel: erwartete Rendite − Risikoterm − [Transaktionskosten](#gloss:transaktionskosten){.glossar-link}`\index{Transaktionskosten}`{=latex} − Steuerwirkung.
+* Solver: [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} (konvexer Teil) oder [MIQP](#gloss:miqp){.glossar-link} für die Kardinalität.
**Abnahmekriterien.**
- [ ] Toleranzband: Es wird nur gehandelt, wenn die Abweichung > 5 Prozentpunkte beträgt.
- [ ] Alle Kosten (Ordergebühr, Spread, Steuer) sind explizit ausgewiesen.
-- [ ] Backtest über 5 Jahre mit Lookahead-Selbsttest.
+- [ ] [Backtest](#gloss:backtest){.glossar-link} über 5 Jahre mit Lookahead-Selbsttest.
- [ ] Vergleich gegen: nie rebalancen, jährlich rebalancen, Gleichgewichtung.
- [ ] Bericht, den ein Nicht-Fachmann versteht.
@@ -25298,18 +25298,18 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
**Ausgangslage.** Für ein bestehendes Portfolio soll ein monatlicher Risikobericht entstehen.
**Inhalte des Berichts.**
-* VaR und CVaR auf 95 % und 99 %, historisch **und** Monte-Carlo-simuliert.
+* [VaR](#gloss:value-at-risk-var){.glossar-link} und [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} auf 95 % und 99 %, historisch **und** Monte-Carlo-simuliert.
* Risikobeiträge je Position (*marginal CVaR*): Wer treibt das Risiko?
* Stresstests: Was passiert bei −20 % Aktienmarkt, +200 Basispunkten Zins, Korrelationsanstieg auf 0,9?
* Historische Szenarien nachspielen (2008, März 2020).
-* Konzentrationskennzahlen (Herfindahl-Index`\index{Herfindahl-Index}`{=latex}, effektive Titelzahl).
+* Konzentrationskennzahlen ([Herfindahl-Index](#gloss:herfindahl-index){.glossar-link}`\index{Herfindahl-Index}`{=latex}, effektive Titelzahl).
**Abnahmekriterien.**
- [ ] Bericht als PDF, automatisch erzeugt.
- [ ] Alle Kennzahlen mit zwei unabhängigen Methoden berechnet und verglichen.
- [ ] Klartext-Zusammenfassung: „Im schlechtesten Prozent der Monate verlieren Sie typischerweise X €.“
-**Stolperfallen.** Die Wurzel-Zeit-Regel`\index{Wurzel-Zeit-Regel}`{=latex} bei CVaR ([Kapitel 20](#kap-cvar)) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
+**Stolperfallen.** Die [Wurzel-Zeit-Regel](#gloss:wurzel-zeit-regel){.glossar-link}`\index{Wurzel-Zeit-Regel}`{=latex} bei CVaR ([Kapitel 20](#kap-cvar)) — nicht blind anwenden. Korrelationen sind in Krisen andere als im Mittel: Stresstest mit erhöhten Korrelationen rechnen.
---
@@ -25320,7 +25320,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
**Ausgangslage.** P3 hat funktioniert — mit 30 Kunden. Der Auftraggeber kommt mit 400 zurück, und derselbe Code liefert nach zwanzig Minuten noch keine Lösung. Das ist keine Panne, sondern der erwartbare Umschlagpunkt.
**Modellskizze.** Zwei Verfahren am **selben** Problem, verglichen unter **gleichem Zeitbudget**:
-* Exakt: MILP oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap.
+* Exakt: [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} oder CP-SAT wie in P3, mit Zeitlimit und protokolliertem Gap.
* Heuristisch: Startlösung mit einer Faustregel, dann Verbesserung durch Simulated Annealing oder Large Neighborhood Search ([Kapitel 9](#kap-metaheuristiken)).
* Messgröße ist nicht „wer gewinnt", sondern **ab welcher Instanzgröße** sich das Blatt wendet.
@@ -25346,7 +25346,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
* Ergebnis ist **keine Lösung, sondern eine Kurve** — plus die Angabe, was jeder eingesparte Kilogramm CO₂ an Mehrkosten bedeutet.
**Abnahmekriterien.**
-- [ ] Die Pareto-Front`\index{Pareto-Front}`{=latex} ist berechnet und gezeichnet, nicht nur beschrieben.
+- [ ] Die [Pareto-Front](#gloss:pareto-front){.glossar-link}`\index{Pareto-Front}`{=latex} ist berechnet und gezeichnet, nicht nur beschrieben.
- [ ] Für mindestens zwei Punkte ist der Aufpreis je eingesparter Einheit ausgerechnet.
- [ ] Es ist geprüft und dokumentiert, welche Punkte der Front eine **gewichtete Summe niemals** finden würde — und warum.
- [ ] Die Entscheidungsvorlage nennt drei Punkte zur Auswahl, nicht dreißig.
@@ -25390,7 +25390,7 @@ Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und ei
| Trainingsplan eines Sportvereins (Hallen, Trainer, Altersgruppen) | CP-SAT | [Kapitel 7](#kap-cpsat) |
| Budgetaufteilung auf Projekte mit Abhängigkeiten | MILP | [Kapitel 6](#kap-milp) |
| Schnittoptimierung für Zuschnitt (Holz, Blech, Stoff) | MILP / Spaltengenerierung | [Kapitel 10](#kap-dekomposition) |
-| Speiseplan unter Nährwert- und Budgetgrenzen | LP | [Kapitel 5](#kap-lp) |
+| Speiseplan unter Nährwert- und Budgetgrenzen | [LP](#gloss:lineare-programmierung-lp){.glossar-link} | [Kapitel 5](#kap-lp) |
| Prüfungsplanung (keine Kollisionen, Erholungspausen) | CP-SAT | [Kapitel 7](#kap-cpsat) |
| Ladeplanung für E-Fahrzeugflotte (Lastspitzen vermeiden) | MILP + Cumulative | [Kapitel 6](#kap-milp), [Kapitel 7](#kap-cpsat) |
| Bewässerungsplan im Kleingarten (Wasser, Wetterprognose) | Stochastisch | [Kapitel 12](#kap-unsicherheit) |
@@ -25428,11 +25428,11 @@ Viel Erfolg.
**1.1 — Analytik-Stufen.** (a) deskriptiv (Vergangenheit beschreiben). (b) prädiktiv (Prognose). (c) **präskriptiv** (Handlungsanweisung unter Restriktionen). (d) prädiktiv (Klassifikation). (e) **präskriptiv** — hier kommt das begrenzte Budget ins Spiel, es muss zugeteilt werden. Beachten Sie das Paar (d)/(e): Die Prognose sagt, *wer* kündigen wird; die Optimierung sagt, *wen* man mit dem vorhandenen Geld halten kann.
-**1.2 — Hart oder weich`\index{Nebenbedingung!weich}`{=latex}?** (a) **hart** — gesetzlich zwingend. (b) **weich** — Wunsch, mit Strafkosten`\index{Strafkosten}`{=latex}. (c) **hart** — Patientensicherheit, rechtlich vorgeschrieben. (d) **weich** — Fairnessziel, über Strafterme. (e) **hart**`\index{Nebenbedingung!hart}`{=latex}, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
+**1.2 — Hart oder weich`\index{Nebenbedingung!weich}`{=latex}?** (a) **hart** — gesetzlich zwingend. (b) **weich** — Wunsch, mit [Strafkosten](#gloss:strafkosten){.glossar-link}`\index{Strafkosten}`{=latex}. (c) **hart** — Patientensicherheit, rechtlich vorgeschrieben. (d) **weich** — Fairnessziel, über Strafterme. (e) **hart**`\index{Nebenbedingung!hart}`{=latex}, falls tariflich/gesetzlich fixiert, sonst weich mit sehr hoher Strafe. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich zu verletzen ist.
**1.3 — Kombinatorik.** (a) $12! = 479\,001\,600$. (b) $479\,001\,600 / 5\cdot10^6 \approx 95{,}8$ Sekunden $\approx 1{,}6$ Minuten. (c) $13! = 6\,227\,020\,800$; das sind $1245$ s $\approx 20{,}8$ Minuten — **Faktor 13**. Jeder weitere Auftrag multipliziert die Zeit mit der neuen Anzahl. (d) Eine Stunde $= 3600$ s $\to 1{,}8\cdot10^{10}$ Prüfungen. $13! = 6{,}2\cdot10^9$ ✓, $14! = 8{,}7\cdot10^{10}$ ✗. Also **13 Aufträge**.
-**1.4 — Modell lesen.** (a) Variablen $x_1, x_2 \ge 0$; Parameter`\index{Parameter (Modell)}`{=latex} $(3,5)$ und die Kapazitäten $(4,12,18)$; Zielfunktion`\index{Zielfunktion}`{=latex} $\max 3x_1+5x_2$; vier Nebenbedingungen inkl. Nichtnegativität. (b) $(2,6)$: $2\le4$ ✓, $12\le12$ ✓, $6+12=18\le18$ ✓ → zulässig, $Z=36$. $(4,3)$: $4\le4$ ✓, $6\le12$ ✓, $12+6=18\le18$ ✓ → zulässig, $Z=27$. (c) Beste ganzzahlige Lösung ist $(2,6)$ mit $Z=36$ — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
+**1.4 — Modell lesen.** (a) Variablen $x_1, x_2 \ge 0$; [Parameter](#gloss:parameter){.glossar-link}`\index{Parameter (Modell)}`{=latex} $(3,5)$ und die Kapazitäten $(4,12,18)$; [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} $\max 3x_1+5x_2$; vier Nebenbedingungen inkl. Nichtnegativität. (b) $(2,6)$: $2\le4$ ✓, $12\le12$ ✓, $6+12=18\le18$ ✓ → zulässig, $Z=36$. $(4,3)$: $4\le4$ ✓, $6\le12$ ✓, $12+6=18\le18$ ✓ → zulässig, $Z=27$. (c) Beste ganzzahlige Lösung ist $(2,6)$ mit $Z=36$ — sie ist hier bereits ganzzahlig, weil die Ecke des Polyeders zufällig auf Gitterpunkten liegt.
**1.5 — Bäckerei.**
@@ -25452,10 +25452,10 @@ s = cp_model.CpSolver(); s.Solve(m)
Probe: Mehl $0{,}5\cdot96 + 0{,}6\cdot70 = 48 + 42 = 90$ kg ✓ (voll ausgelastet); Ofen $4\cdot96 + 3\cdot70 = 384+210 = 594 \le 600$ ✓; Vertrag $96 \ge 40$ ✓; $Z = 2{,}5\cdot96 + 3\cdot70 = 240 + 210 = 450$ ✓.
**Zwei lehrreiche Beobachtungen:**
-1. Die **Vertragsbedingung $x_1 \ge 40$ ist nicht bindend** — der Solver backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch.
+1. Die **Vertragsbedingung $x_1 \ge 40$ ist nicht bindend** — der [Solver](#gloss:solver){.glossar-link} backt freiwillig 96 Brote. Wer aus der Aufgabenstellung schließt, die Mindestmenge werde „gerade so“ erfüllt, liegt falsch.
2. Das **kontinuierliche** Optimum $(40;\ 116{,}67)$ liefert **denselben** Zielwert 450. Das Problem hat also mehrere optimale Lösungen — die Zielfunktion verläuft parallel zur Mehlrestriktion ($2{,}5/0{,}5 = 5 = 3{,}0/0{,}6$). Prüfen Sie das nach: Genau deshalb ist hier auch die ganzzahlige Lösung ohne Verlust erreichbar.
-**1.6 — Sensitivität durch Ausprobieren.** Schleife über `RAM_GESAMT in range(54, 73, 2)`, jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die **vCPU**-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der **Schattenpreis**`\index{Schattenpreis}`{=latex} ([Kapitel 5](#kap-lp)). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
+**1.6 — Sensitivität durch Ausprobieren.** Schleife über `RAM_GESAMT in range(54, 73, 2)`, jeweils Modell neu lösen. (a) Ab 62 GB steigt der Gewinn nicht mehr — dann bindet die **vCPU**-Grenze, RAM ist nicht mehr der Engpass. (b) Der Zuwachs je GB ist der **[Schattenpreis](#gloss:schattenpreis){.glossar-link}**`\index{Schattenpreis}`{=latex} ([Kapitel 5](#kap-lp)). Solange RAM bindet, liegt er bei 31,25 €/GB; danach fällt er auf 0.
**1.7 — Eigenes Problem.** Individuell. Prüfkriterien: Sind die Variablen wirklich *entscheidbar* (nicht bereits festgelegt)? Hat die Zielfunktion eine **Einheit**? Ist jede harte Bedingung wirklich unverhandelbar?
@@ -25509,7 +25509,7 @@ Diese vier Zeilen sind das wichtigste Werkzeug des Kapitels: Sie prüfen die **L
**2.3 — Ecken von Hand.** (b) Ecken: $(0,0)$, $(6,0)$, $(4,4)$, $(0,8)$. (c) $Z$: 0, 12, 20, **24** → Optimum $(0,8)$ mit $Z = 24$. (d) Bei $\max 2x_1+2x_2$: $Z(4,4) = 16$, $Z(0,8) = 16$ — die Zielfunktion ist **parallel zur Kante** $x_1+x_2=8$. Es gibt dann **unendlich viele** optimale Lösungen (die ganze Kante), aber weiterhin mindestens eine in einer Ecke — der Fundamentalsatz bleibt gültig.
-**2.4 — Konvexität.** (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex ($f'' = 12x^2 \ge 0$). (c) **nicht** konvex, sondern **konkav** ($f'' = -\tfrac14 x^{-3/2} < 0$). (d) **nicht** konvex, konkav ($f'' = -1/x^2 < 0$). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix $2\mathbf{I} \succ 0$). (f) konvexe Menge`\index{Konvexität!Menge}`{=latex} (Kreisscheibe). (g) **konvexe Menge** — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die *Funktion*`\index{Konvexität!Funktion}`{=latex} $1/x$ ist konvex, und die Menge $\{x_2 \ge 1/x_1\}$ ist der Epigraph einer konvexen Funktion, also konvex).
+**2.4 — [Konvexität](#gloss:konvexitaet){.glossar-link}.** (a) konvex (linear, sogar affin — Grenzfall, auch konkav). (b) konvex ($f'' = 12x^2 \ge 0$). (c) **nicht** konvex, sondern **konkav** ($f'' = -\tfrac14 x^{-3/2} < 0$). (d) **nicht** konvex, konkav ($f'' = -1/x^2 < 0$). (e) konvex (Summe konvexer Funktionen; Hesse-Matrix $2\mathbf{I} \succ 0$). (f) konvexe Menge`\index{Konvexität!Menge}`{=latex} (Kreisscheibe). (g) **konvexe Menge** — der Bereich oberhalb der Hyperbel im positiven Quadranten ist konvex (Achtung, überraschend: die *Funktion*`\index{Konvexität!Funktion}`{=latex} $1/x$ ist konvex, und die Menge $\{x_2 \ge 1/x_1\}$ ist der Epigraph einer konvexen Funktion, also konvex).
**2.5 — Positive Semidefinitheit.** $\mathbf{P}_1$: Eigenwerte $\approx (3{,}8;\ 9{,}2)$ → PSD ✓, Korrelation $1/\sqrt{4\cdot9} = 0{,}167$ ✓. $\mathbf{P}_2$: Eigenwerte $\approx (-0{,}6;\ 13{,}6)$ → **nicht PSD**; implizierte Korrelation $7/\sqrt{36} = 1{,}167 > 1$ — unmöglich. $\mathbf{P}_3$: Eigenwerte $(0{,}5;\ 0{,}5;\ 2{,}0)$ → PSD ✓.
@@ -25525,7 +25525,7 @@ Für $\mathbf{P}_2$ ergibt sich eine PSD-Matrix mit Korrelation exakt $1{,}0$
### Finde den Denkfehler — Die unauffällige Transposition
-(a) **Warum nichts auffällt.** $\mathbf{A}$ ist quadratisch, also passen die Dimensionen auch transponiert. NumPy prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges LP vorgelegt und löst es korrekt — nur eben ein **anderes**. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos *aussieht*: Wäre die Matrix $2 \times 3$ gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
+(a) **Warum nichts auffällt.** $\mathbf{A}$ ist quadratisch, also passen die Dimensionen auch transponiert. [NumPy](#gloss:numpy){.glossar-link} prüft Formen, nicht Bedeutungen; der Solver bekommt ein vollkommen zulässiges [LP](#gloss:lineare-programmierung-lp){.glossar-link} vorgelegt und löst es korrekt — nur eben ein **anderes**. Eine Transposition ist genau dann gefährlich, wenn sie folgenlos *aussieht*: Wäre die Matrix $2 \times 3$ gewesen, hätte NumPy sofort einen Dimensionsfehler geworfen.
(b) **Der Plan an den echten Restriktionen.** Mit $\mathbf{A} = \begin{pmatrix} 1 & 2 \\ 3 & 1\end{pmatrix}$ und $\mathbf{x} = (5{,}6;\ 0{,}8)$:
@@ -25550,7 +25550,7 @@ Vorbeugend hilft außerdem, Matrizen nie positionsweise abzutippen, sondern übe
**1 — (b).** Der Fundamentalsatz sagt nur, dass ein Optimum *in einer Ecke angenommen wird*. (a) ist falsch, weil es mehrere optimale Ecken geben kann (wenn die Zielfunktion parallel zu einer Kante verläuft) und dann sogar unendlich viele optimale Punkte auf der Verbindungsstrecke. (c) ist falsch, weil die Zahl der Eckenkandidaten kombinatorisch wächst: Bei 6 Variablen und 4 Ungleichungen plus 6 Nichtnegativitäten sind $\binom{10}{6} = 210$ Systeme zu prüfen — bei 50 Variablen wären es $10^{29}$. Der Satz sagt, *wo* man suchen muss, nicht dass die Suche billig ist.
-**2 — (b).** Faustregel $\kappa = 10^{k}$ ⟹ etwa $k$ signifikante Stellen verloren; bei $10^{11}$ bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die Konditionszahl`\index{Konditionszahl}`{=latex} mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
+**2 — (b).** Faustregel $\kappa = 10^{k}$ ⟹ etwa $k$ signifikante Stellen verloren; bei $10^{11}$ bleiben von 16 rund 5. Das Modell ist deshalb nicht unlösbar (a) — es rechnet nur mit einer Genauigkeit, die den Ergebnissen nicht mehr anzusehen ist. (c) verwechselt die [Konditionszahl](#gloss:konditionszahl){.glossar-link}`\index{Konditionszahl}`{=latex} mit einem Aufwandsmaß; sie sagt nichts über die Iterationszahl.
**3 — (c).** `int()` schneidet ab: `int(0.99999998) == 0` macht aus einer Ja- eine Nein-Entscheidung (a). Den Wert unverändert weiterzureichen (b) verschiebt das Problem nur in die nachgelagerte Verarbeitung, wo dann irgendwann doch jemand `int()` schreibt. Richtig ist die Prüfung gegen die Toleranz mit Fehlermeldung im Zweifelsfall — die Funktion `sichere_ganzzahl()` aus `Skalierung_Kondition.py`.
@@ -25565,23 +25565,23 @@ Vorbeugend hilft außerdem, Matrizen nie positionsweise abzutippen, sondern übe
## A.3 Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“ {#sec:loesungen-oekosystem}
-**3.1 — Solverwahl.** (a) CP-SAT`\index{CP-SAT}`{=latex} (diskrete Zuordnung mit Zeitfenstern). (b) `scipy.optimize.linprog` (klassisches Mischungs-LP, klein). (c) CVXPY`\index{CVXPY}`{=latex} (konvexes QP). (d) MIQP — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) MILP über `highspy` oder CP-SAT (Standortproblem mit Fixkosten). (f) `scipy.optimize.minimize` mit Multistart (nicht konvex).
+**3.1 — Solverwahl.** (a) [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} (diskrete Zuordnung mit Zeitfenstern). (b) `scipy.optimize.linprog` (klassisches Mischungs-LP, klein). (c) [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} (konvexes [QP](#gloss:quadratische-programmierung-qp){.glossar-link}). (d) [MIQP](#gloss:miqp){.glossar-link} — CVXPY mit Binärvariablen und MIQP-fähigem Solver, oder Heuristik. (e) [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} über `highspy` oder [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} (Standortproblem mit Fixkosten). (f) `scipy.optimize.minimize` mit Multistart (nicht konvex).
**3.2 — Bäckerei viermal.** Alle vier müssen $Z = 448$ (ganzzahlig) bzw. $450$ (kontinuierlich) liefern. Denken Sie an die Prozesstrennung, falls `ortools` und `highspy` kollidieren.
-**3.3 — CSR-Format`\index{CSR-Format}`{=latex}.** `values = [3, 1, 2, 5, 4, 6]`, `indices = [0, 3, 2, 0, 1, 3]`, `starts = [0, 2, 3]`. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte $3\times4 = 12$. **Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht** — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
+**3.3 — [CSR-Format](#gloss:csr-format){.glossar-link}`\index{CSR-Format}`{=latex}.** `values = [3, 1, 2, 5, 4, 6]`, `indices = [0, 3, 2, 0, 1, 3]`, `starts = [0, 2, 3]`. CSR speichert 6 Werte + 6 Indizes + 3 Startpositionen = 15 Zahlen; die volle Matrix hätte $3\times4 = 12$. **Bei dieser winzigen, dicht besetzten Matrix lohnt CSR nicht** — der Vorteil entsteht erst bei großer, dünn besetzter Struktur (z. B. 1000×1000 mit 0,5 % Besetzung: 15 000 statt 1 000 000 Zahlen).
**3.4 — Konvexitätsprüfung.** $\min x^3$ wirft `DCPError: Problem does not follow DCP rules`, weil $x^3$ auf $[-2,2]$ weder konvex noch konkav ist. $\min x^2$ läuft und liefert $x=0$. Der Unterschied: CVXPY akzeptiert nur Ausdrücke, deren Konvexität es **beweisen** kann — dafür garantiert es das globale Optimum.
**3.5 — Laufzeitvergleich.** Erwartetes Muster: `linprog` und `highspy` liegen bei kleinen Modellen gleichauf (Overhead dominiert); ab etwa $n \gtrsim 500$ zieht `highspy` davon, weil der Modellaufbau effizienter ist. CVXPY hat den größten festen Aufwand (Ausdrucksbaum-Kompilierung), der bei wiederholten Läufen mit `cp.Parameter` teilweise entfällt.
-**3.6 — Eigene Entscheidungshilfe.** Ergänzungen: Bei kommerzieller Lizenz Gurobi`\index{Gurobi}`{=latex}/CPLEX über Pyomo`\index{Pyomo}`{=latex} oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo`\index{Pyomo}`{=latex} (algebraische Notation, Trennung von Daten und Modell).
+**3.6 — Eigene Entscheidungshilfe.** Ergänzungen: Bei kommerzieller Lizenz [Gurobi](#gloss:gurobi){.glossar-link}`\index{Gurobi}`{=latex}/CPLEX über [Pyomo](#gloss:pyomo){.glossar-link}`\index{Pyomo}`{=latex} oder CVXPY empfehlen; bei Lesbarkeitsanforderung Pyomo`\index{Pyomo}`{=latex} (algebraische Notation, Trennung von Daten und Modell).
### Finde den Denkfehler — Der Solver, der angeblich dreimal schneller ist
(a) **Was da alles mitgemessen wird.** Die Stoppuhr läuft ab der ersten Zeile, also mindestens über drei Dinge, die mit Lösegeschwindigkeit nichts zu tun haben:
-1. **Der `import`.** CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. `linprog` steht in einem Prozess, der SciPy ohnehin schon geladen hat, praktisch sofort bereit.
+1. **Der `import`.** CVXPY zieht beim ersten Import seine gesamte Solver-Erkennung hoch — allein das kostet regelmäßig über eine Sekunde. `linprog` steht in einem Prozess, der [SciPy](#gloss:scipy){.glossar-link} ohnehin schon geladen hat, praktisch sofort bereit.
2. **Der Modellaufbau.** CVXPY baut einen Ausdrucksbaum und kompiliert ihn in die Standardform des Solvers. Das ist echter Aufwand — aber Aufbau, nicht Rechnen.
3. **Die Reihenfolge.** CVXPY läuft zuerst und bezahlt dabei alles, was danach im Betriebssystem-Cache liegt: Bibliotheken, Speicherseiten, JIT-Wärme. Tauschen Sie die beiden Blöcke, und die Zahlen verschieben sich allein deshalb.
@@ -25618,14 +25618,14 @@ Und die vierte, wichtigste Regel: **die Größe messen, die später auch läuft.
**2 — (c) Modellaufbau vektorisieren.** 32 von 40 Sekunden fallen an, *bevor* der Solver startet. Ein kommerzieller Solver (a) beschleunigt bestenfalls die verbleibenden 8 Sekunden — selbst bei Faktor 4 gewinnen Sie 6 der 40 Sekunden. Das Zeitlimit (b) betrifft den Abbruch, nicht die Geschwindigkeit. Die Regel dahinter: **erst messen, wo die Zeit hingeht, dann optimieren.**
-**3 — (b).** Alle vier sind Modellierungsschichten über HiGHS`\index{HiGHS}`{=latex}. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver ([Kapitel 11](#kap-qp-nlp)).
+**3 — (b).** Alle vier sind Modellierungsschichten über [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex}. Deshalb liefern sie denselben Zielwert (bis auf Toleranz) und unterscheiden sich in der reinen Rechenzeit kaum — wohl aber im Aufbauaufwand und in der Lesbarkeit. (a) ist falsch: Niemand von ihnen implementiert den Simplex in Python, das wäre um Größenordnungen zu langsam. (c) ist falsch: Nicht-konvexe Probleme global zu lösen kann keines dieser Werkzeuge — dafür braucht es spezialisierte globale Solver ([Kapitel 11](#kap-qp-nlp)).
### Selbsttest {#selbsttest-loesung-oekosystem}
1. Modellierungsschicht (Python) und Solver-Schicht (C++). Die Trennung erlaubt Solvertausch ohne Modelländerung.
2. CVXPY prüft die Konvexität und lehnt ab, was es nicht garantieren kann; `minimize` prüft nichts und liefert ein lokales Optimum.
3. Werte, Spaltenindizes und Zeilenstartpositionen der Nicht-Null-Einträge. Entscheidend, weil reale Modelle extrem dünn besetzt sind.
4. Wegen des Kompilierungsaufwands des Ausdrucksbaums. Kein Argument dagegen, weil dieser Aufwand einmalig ist, die Lesbarkeit hoch und die Konvexitätsprüfung wertvoll.
-5. Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die Routing-Bibliothek`\index{Routing-Bibliothek}`{=latex} überlegen).
+5. Bei rein kontinuierlichen Problemen (CP-SAT kennt nur ganze Zahlen) und bei reinen Routing-Problemen (dort ist die [Routing-Bibliothek](#gloss:routing-bibliothek){.glossar-link}`\index{Routing-Bibliothek}`{=latex} überlegen).
---
@@ -25718,7 +25718,7 @@ Im schwachen Monat ist die Vorgabe schlicht nicht erfüllbar — es gibt nicht g
**5.3 — Simplex von Hand.** Starttableau mit $s_1, s_2$ in der Basis. Erste Iteration: Pivotspalte`\index{Simplex-Algorithmus!Pivotspalte}`{=latex} $x_1$ ($-5$), Quotienten $24/6 = 4$ und $6/1 = 6$ → Pivotzeile`\index{Simplex-Algorithmus!Pivotzeile}`{=latex} 1. Nach dem Tausch: $x_1 = 4$, $Z = 20$. Zweite Iteration: Pivotspalte $x_2$, Pivotzeile 2 → $x_1 = 3$, $x_2 = 1{,}5$, $Z = 21$. **Optimum:** $\mathbf{x}^* = (3;\ 1{,}5)$, $Z^* = 21$, Schattenpreise $y^* = (0{,}75;\ 0{,}50)$.
-**5.4 — Duales Problem`\index{Duales Problem}`{=latex}.** $\min 24y_1 + 6y_2$ u. d. N. $6y_1 + y_2 \ge 5$, $4y_1 + 2y_2 \ge 4$, $y \ge 0$. Lösung $y^* = (0{,}75;\ 0{,}50)$. Probe der Restriktionen: $6\cdot0{,}75 + 0{,}5 = 5$ ✓ (mit Gleichheit, weil $x_1 > 0$), $4\cdot0{,}75 + 2\cdot0{,}5 = 4$ ✓ (ebenfalls Gleichheit, weil $x_2 > 0$). Zielwert $24\cdot0{,}75 + 6\cdot0{,}5 = 18 + 3 = 21 = Z^*$ ✓ — starker Dualitätssatz bestätigt.
+**5.4 — [Duales Problem](#gloss:duales-problem){.glossar-link}`\index{Duales Problem}`{=latex}.** $\min 24y_1 + 6y_2$ u. d. N. $6y_1 + y_2 \ge 5$, $4y_1 + 2y_2 \ge 4$, $y \ge 0$. Lösung $y^* = (0{,}75;\ 0{,}50)$. Probe der Restriktionen: $6\cdot0{,}75 + 0{,}5 = 5$ ✓ (mit Gleichheit, weil $x_1 > 0$), $4\cdot0{,}75 + 2\cdot0{,}5 = 4$ ✓ (ebenfalls Gleichheit, weil $x_2 > 0$). Zielwert $24\cdot0{,}75 + 6\cdot0{,}5 = 18 + 3 = 21 = Z^*$ ✓ — starker Dualitätssatz bestätigt.
**5.5 — Unbeschränktheit.** Der Solver wirft „Problem ist unbeschränkt“. Geometrisch: Der zulässige Bereich $\{x_1 - x_2 \le 5,\ x \ge 0\}$ ist nach oben offen — man kann $x_1$ und $x_2$ gemeinsam beliebig wachsen lassen (z. B. $x_1 = x_2 = t$ für $t \to \infty$), ohne eine Bedingung zu verletzen, und $Z = 2t$ wächst mit. In der Praxis fehlt fast immer eine Kapazitätsgrenze.
@@ -25763,7 +25763,7 @@ Das ist die allgemeine Regel für jede Sensitivitätsaussage über mehr als eine
**1 — (b).** Genau der Satz vom komplementären Schlupf: $s_i \cdot y_i = 0$. Ist Schlupf vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil verkehrt; (c) verwechselt Schlupf mit Auslastung — $s_i = 12$ heißt gerade, dass 12 Einheiten *übrig* sind.
-**2 — (b) Entartung`\index{Entartung}`{=latex}.** 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
+**2 — (b) [Entartung](#gloss:entartung){.glossar-link}`\index{Entartung}`{=latex}.** 7 aktive Bedingungen bei 5 Variablen bedeuten: Die Ecke ist überbestimmt, es gibt mehrere optimale Dualvektoren, und welchen der Solver zeigt, hängt vom gewählten Verfahren ab. (a) verharmlost genau den Fall, der Fehlentscheidungen produziert; (c) verwechselt Entartung mit Unlösbarkeit — der Plan selbst ist völlig in Ordnung und eindeutig.
**3 — (b).** Der Schlupf ist Solver-Rauschen in der Größenordnung der Maschinengenauigkeit — rechnerisch null, aber nicht `== 0.0`. Richtig ist `abs(schlupf) < 1e-7`. (a) unterstellt dem Solver einen Fehler, den er nicht gemacht hat; (c) ist frei erfunden — Schlupfwerte sind bei korrekt aufgestelltem Modell nie negativ, abgesehen von genau diesem Rauschen.
@@ -25780,11 +25780,11 @@ Das ist die allgemeine Regel für jede Sensitivitätsaussage über mehr als eine
**6.1 — Runden widerlegen.** Beispiel: $\max x_1 + x_2$ u. d. N. $10x_1 + 10x_2 \le 15$, ganzzahlig. LP-Optimum $Z = 1{,}5$; Abrunden ergibt $(0,0)$ mit $Z = 0$ — **100 % Verlust**. Das ganzzahlige Optimum ist $(1,0)$ mit $Z = 1$. Prinzip: kleine Zahlen plus knappe Kapazität.
-**6.2 — Big-M wählen.** $M = 250$ (die bekannte Kapazität). Bei $M = 10^6$ bleibt das Modell korrekt, aber die LP-Relaxation`\index{LP-Relaxation}`{=latex} wird extrem schwach: $y_j$ darf schon bei $x_j/10^6$ liegen, die Schranke ist praktisch wertlos, und Branch-and-Bound muss weit mehr Knoten durchsuchen.
+**6.2 — Big-M wählen.** $M = 250$ (die bekannte Kapazität). Bei $M = 10^6$ bleibt das Modell korrekt, aber die [LP-Relaxation](#gloss:lp-relaxation){.glossar-link}`\index{LP-Relaxation}`{=latex} wird extrem schwach: $y_j$ darf schon bei $x_j/10^6$ liegen, die Schranke ist praktisch wertlos, und [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link} muss weit mehr Knoten durchsuchen.
**6.3 — Regeln übersetzen.** (a) $y_A + y_B \le 1$. (b) $\sum_{j=1}^4 y_j \ge 2$. (c) $y_1 + y_2 - 1 \le y_{\text{Lager}}$. (d) $500\,y \le x \le 2000\,y$ mit $y \in \{0,1\}$. (e) $\sum_{j=1}^3 y_j = 1$.
-**6.4 — Branch-and-Bound.** LP-Relaxation`\index{Relaxation}`{=latex} der Wurzel: nach Nutzen/Gewicht sortieren ($8/5=1{,}6$; $11/7=1{,}57$; $6/4=1{,}5$; $4/3=1{,}33$). Gierig füllen: $x_1=1$ (Rest 9), $x_2=1$ (Rest 2), $x_3 = 0{,}5$ → $Z_{LP} = 8+11+3 = 22$. Verzweigen über $x_3$. Ast $x_3=0$: $x_1=1,x_2=1,x_4=2/3$ → $Z = 21{,}67$; weiter verzweigen → beste ganzzahlige Lösung $(1,1,0,0)$ mit $Z=19$. Ast $x_3=1$: $x_1=1$, $x_3=1$, Rest 5 → $x_2=5/7$ → $Z = 21{,}86$; verzweigen führt auf $(1,0,1,1)$ mit $Z = 18$ und $(0,1,1,0)$ mit $Z=17$. **Optimum: $(1,1,0,0)$, $Z = 19$.**
+**6.4 — Branch-and-Bound.** LP-[Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} der Wurzel: nach Nutzen/Gewicht sortieren ($8/5=1{,}6$; $11/7=1{,}57$; $6/4=1{,}5$; $4/3=1{,}33$). Gierig füllen: $x_1=1$ (Rest 9), $x_2=1$ (Rest 2), $x_3 = 0{,}5$ → $Z_{LP} = 8+11+3 = 22$. Verzweigen über $x_3$. Ast $x_3=0$: $x_1=1,x_2=1,x_4=2/3$ → $Z = 21{,}67$; weiter verzweigen → beste ganzzahlige Lösung $(1,1,0,0)$ mit $Z=19$. Ast $x_3=1$: $x_1=1$, $x_3=1$, Rest 5 → $x_2=5/7$ → $Z = 21{,}86$; verzweigen führt auf $(1,0,1,1)$ mit $Z = 18$ und $(0,1,1,0)$ mit $Z=17$. **Optimum: $(1,1,0,0)$, $Z = 19$.**
**6.5 — Kardinalität variieren.** (a) Ab $K = 3$ steigt der Ertrag nicht mehr wesentlich, weil bereits drei Positionen à 40 000 € das Budget von 100 000 € abdecken können. (b) Rechenzeit steigt zunächst (mehr Kombinationen), fällt bei großem $K$ wieder (Restriktion bindet nicht mehr). (c) Der faire Preis ist die Differenz der Netto-Erträge zwischen $K=3$ und $K=4$ — bei den gegebenen Daten nahe null, weil die Obergrenze von 40 000 € bereits bindet.
@@ -25832,9 +25832,9 @@ Test 3 ist der wichtigste. Er vergleicht nicht Modell mit Modell, sondern **Mode
**1 — (b).** Der Gap $(48\,200 - 47\,100)/48\,200 = 2{,}3\,\%$ ist eine **Garantie**, keine Schätzung: Besser als 47 100 € kann es nachweislich nicht werden, also ist der vorliegende Plan höchstens 2,3 % zu teuer. (a) wirft eine völlig brauchbare Lösung weg — genau der Fehler, den `if status == OPTIMAL: ... else: return None` produziert. (c) verwechselt die Schranke mit einem erreichbaren Wert: 47 100 € ist eine *untere* Schranke, es ist völlig offen, ob ein Plan mit diesen Kosten überhaupt existiert.
-**2 — (b).** Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der Trickle Flow`\index{Trickle Flow}`{=latex} dagegen ist **still**: Das Modell meldet `Optimal`, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
+**2 — (b).** Die Laufzeit (a) ist ein realer, aber beherrschbarer Nachteil — man merkt ihn und kann reagieren. Der [Trickle Flow](#gloss:trickle-flow){.glossar-link}`\index{Trickle Flow}`{=latex} dagegen ist **still**: Das Modell meldet `Optimal`, liefert eine Zahl, und niemand sieht, dass die Fixkosten verschwunden sind. Ein Fehler, den man bemerkt, ist immer harmloser als einer, den man nicht bemerkt. (c) ist frei erfunden — negative Kosten entstehen dabei nicht.
-**3 — (c).** Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und `INFEASIBLE` bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen Warm-Start`\index{Warm-Start}`{=latex}-Effekt, den es gar nicht gibt.
+**3 — (c).** Ein Hinweis ist für CP-SAT unverbindlich: Er wird als Startlösung ausprobiert und verworfen, wenn er nicht zulässig ist. Das Ergebnis bleibt in jedem Fall korrekt (b ist also falsch), und `INFEASIBLE` bezieht sich immer auf das Modell, nie auf einen Hinweis (a ist falsch). Praktische Konsequenz: Prüfen Sie Ihre Startlösung selbst auf Zulässigkeit — sonst messen Sie einen [Warm-Start](#gloss:warm-start){.glossar-link}`\index{Warm-Start}`{=latex}-Effekt, den es gar nicht gibt.
### Selbsttest {#selbsttest-loesung-milp}
1. Weil die Relaxation **mehr** Lösungen zulässt (alle ganzzahligen plus gebrochene) — das Maximum über einer größeren Menge ist mindestens so groß.
@@ -25853,7 +25853,7 @@ Test 3 ist der wichtigste. Er vergleicht nicht Modell mit Modell, sondern **Mode
**7.3 — Regeln ergänzen.** (a) `for s in (2,3): modell.Add(x["Frau_Albrecht", s] == 0)` (b) Hilfsvariablen `arbeitet_bauer`, `arbeitet_koch` per `AddMaxEquality` an die Zuweisungssummen koppeln, dann `modell.Add(arbeitet_bauer + arbeitet_koch <= 1)`. (c) Belohnung = negative Strafe: `strafterme.append(-30 * folge_var)`, wobei `folge_var` per `AddBoolAnd`/`OnlyEnforceIf` an $x_{p,0} \wedge x_{p,1}$ gekoppelt wird.
-**7.4 — Infeasibility`\index{Infeasibility}`{=latex}.** Mit `MAX_VERTRETUNGEN = 0` meldet der Solver `INFEASIBLE`. Nach Einbau der Schlupfvariablen lässt der Solver **alle vier** Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei `MAX_VERTRETUNGEN = 1`: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
+**7.4 — [Infeasibility](#gloss:infeasibility){.glossar-link}`\index{Infeasibility}`{=latex}.** Mit `MAX_VERTRETUNGEN = 0` meldet der Solver `INFEASIBLE`. Nach Einbau der Schlupfvariablen lässt der Solver **alle vier** Stunden ausfallen (4 × 10 000 = 40 000 Strafpunkte) — es geht nicht anders. Interessanter wird es bei `MAX_VERTRETUNGEN = 1`: Dann fällt genau die Stunde aus, für die am wenigsten qualifiziertes Personal zur Verfügung steht.
**7.5 — Sudoku.**
```python
@@ -25876,7 +25876,7 @@ for i in range(9):
| **1 Arbeiter** | reproduzierbar | 5 Pläne aus 5 Läufen |
| **mehrere Arbeiter** | 3 Pläne aus 4 Läufen | erst recht nicht |
-Nur die Kombination trägt. (c) Bei **15 Aufträgen** (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet `FEASIBLE` mit Makespan`\index{Makespan}`{=latex} 200; acht Arbeiter melden `OPTIMAL` mit **demselben** Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im **Beweis**, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
+Nur die Kombination trägt. (c) Bei **15 Aufträgen** (60 s Zeitlimit): Ein Arbeiter läuft ins Limit und meldet `FEASIBLE` mit [Makespan](#gloss:makespan){.glossar-link}`\index{Makespan}`{=latex} 200; acht Arbeiter melden `OPTIMAL` mit **demselben** Makespan 200 nach 26,4 s. Bei 13 Aufträgen sind es 11,2 s gegen 2,8 s, bei 14 schon 21,4 s gegen 3,7 s. Bemerkenswert ist, dass beide denselben Wert finden — der Unterschied liegt nicht in der Lösung, sondern im **Beweis**, dass es keine bessere gibt. Genau diese Arbeit teilen sich die parallelen Stränge auf.
**7.7 — Job-Shop erweitern.** (a) Rüstzeiten: `AddNoOverlap` durch paarweise Disjunktionen mit Übergangszeit ersetzen, oder `AddCircuit` je Maschine mit Übergangsmatrix. (b) Verspätung: `tardiness = MaxEquality(0, ende - faellig)`, in die Zielfunktion. (c) `AddCumulative(intervalle, [1]*n, 2)` statt `AddNoOverlap` für die Doppelmaschine.
@@ -25923,7 +25923,7 @@ Das ist eine Frage, die eine Zahl als Antwort hat. Und wenn der Auftraggeber sie
**2 — (b).** `AddNoOverlap` ist kompakter *und* propagiert stärker, weil der spezialisierte Propagator alle Intervalle gemeinsam betrachtet statt paarweise. (a) ist falsch: Beide Formulierungen beschreiben dieselbe Menge zulässiger Lösungen, also auch dasselbe Optimum — der Unterschied liegt in der Laufzeit, nicht in der Qualität. (c) ist falsch: CP-SAT rechnet ausschließlich mit ganzen Zahlen, kontinuierliche Zeiten kann es gerade **nicht**.
-**3 — (b).** Mehrfache Optima sind bei Scheduling der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: `assert plan == erwarteter_plan` besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen `assert makespan == 11` und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert `num_workers` und `random_seed`.
+**3 — (b).** Mehrfache Optima sind bei [Scheduling](#gloss:scheduling){.glossar-link} der Normalfall, kein Fehler. (a) verwechselt Eindeutigkeit mit Korrektheit; (c) unterstellt ein Genauigkeitsproblem, das es nicht gibt — alle gefundenen Pläne haben exakt denselben Makespan. Praktische Konsequenz: `assert plan == erwarteter_plan` besteht mal und scheitert mal, ohne dass sich am Code etwas geändert hat. Prüfen Sie stattdessen `assert makespan == 11` und die Einhaltung aller Regeln. Wer eine reproduzierbare Ausgabe braucht — etwa für ein Buch —, fixiert `num_workers` und `random_seed`.
### Selbsttest {#selbsttest-loesung-cpsat}
1. Sie entfernt Werte aus den Wertebereichen, die aufgrund der Bedingungen unmöglich sind — **bevor** gesucht wird. Dadurch schrumpft der Suchbaum drastisch.
@@ -25938,7 +25938,7 @@ Das ist eine Frage, die eine Zahl als Antwort hat. Und wenn der Auftraggeber sie
## A.8 Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“ {#sec:loesungen-graphen}
-**8.1 — Flusserhaltung`\index{Flusserhaltung}`{=latex}.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
+**8.1 — [Flusserhaltung](#gloss:flusserhaltung){.glossar-link}`\index{Flusserhaltung}`{=latex}.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
**8.2 — Unlösbarkeit.** Die Summe aller Flusserhaltungsgleichungen ergibt $\sum_i b_i = 0$ (jede Kante taucht einmal mit $+1$ und einmal mit $-1$ auf). Ist die Summe ungleich null, widersprechen sich die Gleichungen. Bei Angebotsüberschuss führt man einen künstlichen **Dummy-Senkenknoten** mit dem Restbedarf und Kosten 0 ein.
@@ -25957,9 +25957,9 @@ Beachten Sie, dass Werk 3 seine günstigste Verbindung (L4 zu 5) voll ausschöpf
**8.5 — Engpass finden.** Die Dualwerte der Kapazitätsschranken (`res.upper.marginals` bei `linprog`) zeigen es direkt; alternativ jede Kapazität einzeln um 1 erhöhen und neu rechnen. Im Beispiel ist die Kante `Werk_A → Umschlag` der Engpass (voll ausgelastet, günstigster Weg).
-**8.6 — VRP variieren.** (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald $\text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf}$ (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) **oder** wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die Metaheuristik`\index{Metaheuristik}`{=latex} verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
+**8.6 — [VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link} variieren.** (a) Mit 3 Fahrzeugen werden die Touren länger, die Gesamtfahrzeit steigt leicht, die Auslastung deutlich. (b) Unlösbar, sobald $\text{Kapazität} \times \text{Fahrzeuge} < \text{Gesamtbedarf}$ (hier: bei 3 Fahrzeugen à 10 sind 30 < 37 → unlösbar) **oder** wenn Zeitfenster nicht mehr eingehalten werden können. (c) 1 s liefert meist eine brauchbare, 30 s eine spürbar bessere Lösung — die [Metaheuristik](#gloss:metaheuristik){.glossar-link}`\index{Metaheuristik}`{=latex} verbessert kontinuierlich. (d) Doppelte Servicezeit kann Zeitfenster verletzen → möglicherweise unlösbar.
-**8.7 — TSP mit MTZ.** Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während OR-Tools`\index{OR-Tools}`{=latex} weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
+**8.7 — [TSP](#gloss:tsp-traveling-salesperson-problem){.glossar-link} mit MTZ.** Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 12–15 Städten wird die schwache MTZ-Relaxation zum Problem, und die Laufzeit steigt stark — während [OR-Tools](#gloss:or-tools){.glossar-link}`\index{OR-Tools}`{=latex} weiterhin in Sekundenbruchteilen sehr gute Touren liefert.
### Finde den Denkfehler — Die vergessene Dimension
@@ -26033,7 +26033,7 @@ Alles, was der Kollege sagt, stimmt. Der Fehler liegt darin, was er *nicht* sagt
`Metaheuristik_vs_Exakt.py` liefert die fehlende Zahl in der Spalte *Schranke*: **1 768 Minuten**. Der Abstand der vorgestellten Lösung zum Bestmöglichen beträgt also bis zu 25 %, nicht 0 %. In derselben Nacht, in der 640 Stunden gefeiert werden, liegen möglicherweise weitere 2 000 Stunden ungenutzt herum.
-Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist LNS.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
+Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden verdient. Die Konsequenz ist eine ehrliche Fortschreibung: „Wir heben 6,2 % und wissen, dass bis zu einem Viertel noch offen ist. Der nächste Schritt ist [LNS](#gloss:large-neighborhood-search-lns){.glossar-link}.“ Wer die Schranke verschweigt, erklärt das Projekt für abgeschlossen und lässt den größeren Teil liegen.
> Dasselbe Muster in anderer Verkleidung: Kapitel Handelsmaschine, `Data_Snooping.py` — dort fehlt nicht die Schranke, sondern die Zahl der Versuche. Beide Male macht eine weggelassene Kennzahl aus einem korrekten Ergebnis eine irreführende Aussage.
@@ -26098,7 +26098,7 @@ Nachgerechnet mit denselben 38 Mustern:
Der Fehler steckt in der Vorstellung, „aufrunden" koste höchstens eine Rolle. Aufgerundet wird nicht **eine** Zahl, sondern **dreizehn** — je Muster eine. Jede einzelne Aufrundung kostet bis zu eine Rolle, und in der Summe sind es sechs.
-Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**: *Jede zulässige Lösung liegt höchstens eine Rolle darüber.* Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim MIP-Gap`\index{MIP-Gap}`{=latex} in [Abschnitt 6.8](#sec:milp-gap), nur in die andere Richtung.
+Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**: *Jede zulässige Lösung liegt höchstens eine Rolle darüber.* Das ist die Verwechslung von unterer Schranke und Gütegarantie — dieselbe Verwechslung wie beim [MIP-Gap](#gloss:mip-gap){.glossar-link}`\index{MIP-Gap}`{=latex} in [Abschnitt 6.8](#sec:milp-gap), nur in die andere Richtung.
**Was der Kollege stattdessen hätte tun müssen:** das Master ein zweites Mal lösen, diesmal mit `integrality=1` über genau die Spalten, die er ohnehin schon erzeugt hat. Das ist eine Zeile, dauert Millisekunden und liefert 73 statt 79. Er hatte alles dafür bereits vorliegen.
@@ -26108,7 +26108,7 @@ Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**:
**1. b)** Das Mustermodell kennt keine einzelnen Rollen und damit keine austauschbaren Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund; (c) ist falsch, die LP-Lösung ist hier gerade **nicht** ganzzahlig (72,92).
-**2. b)** Ein Rucksackproblem`\index{Rucksackproblem}`{=latex}: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
+**2. b)** Ein [Rucksackproblem](#gloss:rucksackproblem){.glossar-link}`\index{Rucksackproblem}`{=latex}: Nutzen sind die Schattenpreise, Gewichte die Breiten, Kapazität die Rollenbreite. (c) beschreibt eine Heuristik — dann wäre das Verfahren nicht mehr exakt, weil man nie sicher wüsste, ob es wirklich kein lohnendes Muster mehr gibt.
**3. b)** Die untere Schranke von 33,60 beweist, dass mindestens 34 Rollen nötig sind. Damit weiß man, dass die 35 der Faustregel höchstens eine daneben liegen — und kann aufhören zu suchen. Genau diese Aussage kann eine Heuristik allein nie liefern ([Kapitel 9](#kap-metaheuristiken)).
@@ -26126,9 +26126,9 @@ Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**:
**11.1 — Konvexität einordnen.** PSD (alle $\ge 0$) → **konvex**, aber **nicht streng** konvex; die Lösung ist **nicht notwendig eindeutig**. Der Eigenwert 0 bedeutet eine **flache Richtung**: Entlang des zugehörigen Eigenvektors ändert sich der quadratische Term nicht — es gibt eine Rinne statt eines Punktes.
-**11.2 — Komplementärer Schlupf`\index{Komplementärer Schlupf}`{=latex}.** Ja, verträglich: $\lambda_i w_i = 0$ gilt für alle $i$ ($w_2 = 0$ mit $\lambda_2 > 0$; die anderen mit $\lambda = 0$). $\lambda_2 = 0{,}03$ bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort **bindend**.
+**11.2 — [Komplementärer Schlupf](#gloss:komplementaerer-schlupf){.glossar-link}`\index{Komplementärer Schlupf}`{=latex}.** Ja, verträglich: $\lambda_i w_i = 0$ gilt für alle $i$ ($w_2 = 0$ mit $\lambda_2 > 0$; die anderen mit $\lambda = 0$). $\lambda_2 = 0{,}03$ bedeutet: Würde man Titel 2 zwingen, ein kleines positives Gewicht zu tragen, verschlechterte sich der Zielwert um 0,03 je Einheit — die Nichtnegativitätsschranke ist dort **bindend**.
-**11.3 — KKT von Hand.** $\mathcal{L} = x_1^2 + x_2^2 + \lambda(4 - x_1 - x_2)$. Stationarität: $2x_1 = \lambda$, $2x_2 = \lambda$ → $x_1 = x_2$. Bindend ($\lambda > 0$): $x_1 + x_2 = 4$ → $x_1 = x_2 = 2$, $\lambda = 4$. Prüfung: $\lambda = 4 > 0$ ✓, $f = 8$. **Interpretation:** Würde die Forderung auf $\ge 4{,}1$ steigen, stiege $f$ um etwa $4 \cdot 0{,}1 = 0{,}4$. Probe: $2\cdot(2{,}05)^2 = 8{,}405$ ✓.
+**11.3 — [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link} von Hand.** $\mathcal{L} = x_1^2 + x_2^2 + \lambda(4 - x_1 - x_2)$. Stationarität: $2x_1 = \lambda$, $2x_2 = \lambda$ → $x_1 = x_2$. Bindend ($\lambda > 0$): $x_1 + x_2 = 4$ → $x_1 = x_2 = 2$, $\lambda = 4$. Prüfung: $\lambda = 4 > 0$ ✓, $f = 8$. **Interpretation:** Würde die Forderung auf $\ge 4{,}1$ steigen, stiege $f$ um etwa $4 \cdot 0{,}1 = 0{,}4$. Probe: $2\cdot(2{,}05)^2 = 8{,}405$ ✓.
**11.4 — Unmögliche Korrelationsmatrix.** Eigenwerte $\approx (-0{,}62;\ 1{,}0;\ 2{,}62)$ → **nicht PSD**, also unmöglich. Anschaulich: Wenn A stark **positiv** mit B korreliert und B stark positiv mit C, kann A nicht gleichzeitig stark **negativ** mit C korrelieren — Korrelation ist eingeschränkt transitiv. Formal: $\rho_{AC} \ge \rho_{AB}\rho_{BC} - \sqrt{(1-\rho_{AB}^2)(1-\rho_{BC}^2)} = 0{,}81 - 0{,}19 = 0{,}62$; verlangt waren $-0{,}9$.
@@ -26169,7 +26169,7 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R
1. **Prüfen**, ob die Matrix positiv semidefinit ist (Zeile aus (b)) — beim Einlesen, nicht beim Optimieren.
2. **Die Ursache suchen.** Korrelationen aus verschiedenen Quellen, Zeiträumen oder Frequenzen sind fast nie widerspruchsfrei. Schätzen Sie die Matrix aus **einem** konsistenten Datensatz.
-3. Ist das nicht möglich, die Matrix **reparieren**: negative Eigenwerte auf null setzen und rekonstruieren (*Eigenwert-Clipping*, siehe die Aufgabe *Konvexität einer Kovarianzmatrix reparieren* in [Kapitel 2](#kap-fundament)) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert ([Kapitel 18](#kap-finanzdaten)).
+3. Ist das nicht möglich, die Matrix **reparieren**: negative Eigenwerte auf null setzen und rekonstruieren (*Eigenwert-Clipping*, siehe die Aufgabe *Konvexität einer [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link} reparieren* in [Kapitel 2](#kap-fundament)) oder gleich einen Shrinkage-Schätzer verwenden, der positive Semidefinitheit garantiert ([Kapitel 18](#kap-finanzdaten)).
> **Der Merksatz dazu:** Eine Kovarianzmatrix ist kein Behälter für einzeln geschätzte Zahlen, sondern ein geometrisches Objekt. Nicht jede Kombination von Korrelationen existiert.
@@ -26177,7 +26177,7 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R
**1 — (b).** Der `DCPError` ist eine inhaltliche Aussage: Für diese Formulierung kann es keine Optimalitätsgarantie geben. Auf `scipy` auszuweichen (a) beseitigt die Meldung, nicht die Ursache — man bekommt dann ein lokales Ergebnis ohne Garantie und ohne Warnung, wie [Abschnitt 11.8](#sec:qp-nlp-denkfehler) zeigt. Toleranzen (c) haben mit Konvexität nichts zu tun. Richtig ist, zwischen zwei bewussten Wegen zu wählen: konvex umformulieren, oder lokal rechnen **und** das im Bericht kenntlich machen.
-**2 — (b) ein lokales Minimum.** `success: True` beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von SLSQP`\index{SLSQP}`{=latex} eingehalten.
+**2 — (b) ein lokales Minimum.** `success: True` beschreibt die Konvergenz des Verfahrens, nicht die Qualität des Ergebnisses. Bei Mengenrabatten ist die Zielfunktion nicht konvex, also kann es mehrere lokale Minima geben — im Kapitelbeispiel fünf, mit 8 % Spanne. (a) wäre nur bei einem konvexen Problem richtig. (c) ist zu pessimistisch: Ein lokales Minimum ist es sehr wohl, die Zulässigkeit wird von [SLSQP](#gloss:slsqp){.glossar-link}`\index{SLSQP}`{=latex} eingehalten.
**3 — (c) Schattenpreis.** $\lambda^*$ ist die Ableitung des optimalen Zielwerts nach der rechten Seite der Nebenbedingung — dieselbe Bedeutung wie der Dualwert im LP ([Kapitel 5](#kap-lp)), nur für allgemeine, auch krumme Nebenbedingungen. (a) und (b) verwechseln den Multiplikator mit einer Verletzungszahl beziehungsweise mit dem Schlupf; der Schlupf ist bei einer bindenden Bedingung gerade **null** (komplementärer Schlupf).
@@ -26194,17 +26194,17 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R
**12.1 — Fluch des Durchschnitts.** Weil sich Verzögerungen **fortpflanzen** und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (der nächste Schritt ist noch nicht bereit), ein verspätetes verzögert alles. Weitere Beispiele: Wartezeiten in Warteschlangen (nichtlinear in der Auslastung), Projektkosten mit Nachträgen.
-**12.2 — Ansatz wählen.** (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint`\index{Chance Constraint}`{=latex} — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
+**12.2 — Ansatz wählen.** (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) [Chance Constraint](#gloss:chance-constraint){.glossar-link}`\index{Chance Constraint}`{=latex} — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).
**12.3 — Zweistufig rechnen.** (a) Mit Spot = 60: Ableitung bei $x \in (100,250)$: $40 - 60\cdot0{,}5 + 5\cdot0{,}5 = 40-30+2{,}5 = +12{,}5 > 0$ → Kapazität **senken**. Bei $x < 100$: $40 - 60 = -20 < 0$ → erhöhen. Optimum daher $x^* = 100$. (b) Billige Nachbesserung macht Vorhalten unattraktiv. (c) Bei $x = 225$ optimal müsste die Ableitung dort das Vorzeichen wechseln — das ist bei diskreten Szenarien nur zufällig der Fall; der Mittelwert ist nur bei symmetrischen Kosten und stetiger Verteilung optimal.
-**12.4 — EVPI interpretieren.** Selbst eine **perfekte** Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also **niemals** rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
+**12.4 — [EVPI](#gloss:evpi-expected-value-of-perfect-information){.glossar-link} interpretieren.** Selbst eine **perfekte** Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also **niemals** rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“
**12.5 — Monte-Carlo erweitern.** (a) Servicelevel 95 %: Kapazität = 95 %-Quantil des Bedarfs (ca. 485). Die Zusatzkosten gegenüber dem Kostenoptimum (180) betragen ein Vielfaches — Servicelevel ist teuer, und genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (b) `cvar = kosten[kosten >= np.quantile(kosten, 0.95)].mean()`. (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben.
**12.6 — Den Preis der Zusage selbst bestimmen.** (a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die Norm, tragen zur gesicherten Leistung aber kaum bei. (b) Der Unterschied ist **nicht** numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert selbst der **Vollausbau** in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese Fälle in den Daten und meldet korrekt `infeasible`, sobald die Zusage über 92 % steigt. Das analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet `optimal` — und der Plan hält gemessen 87,4 %. Glauben sollte man dem Szenariomodell: Ein `infeasible` ist hier die **richtige** Antwort. Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. (c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“
-**12.7 — Budgeted Uncertainty`\index{Budgeted Uncertainty}`{=latex}.**
+**12.7 — [Budgeted Uncertainty](#gloss:budgeted-uncertainty){.glossar-link}`\index{Budgeted Uncertainty}`{=latex}.**
```python
abzug = cp.sum_largest(cp.multiply(UNSICHERHEIT, w), Gamma)
ziel = cp.Maximize(MU_SCHAETZUNG @ w - abzug - 0.5*LAMBDA*cp.quad_form(w, SIGMA))
@@ -26221,7 +26221,7 @@ $$
Anschaulich: Das Projekt ist fertig, wenn das **letzte** Gewerk fertig ist. Ein Gewerk, das statt 11 nur 7 Tage braucht, bringt niemandem etwas — die anderen sind ja noch dran. Ein Gewerk, das 17 Tage braucht, hält alle auf. Die guten Ausreißer verpuffen, die schlechten schlagen voll durch.
-Damit gilt für das Maximum immer $\mathbb{E}[\max] \ge \max(\mathbb{E})$. Die Differenz ist kein Schätzfehler`\index{Schätzfehler}`{=latex}, sondern eine **systematische Verzerrung** — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
+Damit gilt für das Maximum immer $\mathbb{E}[\max] \ge \max(\mathbb{E})$. Die Differenz ist kein [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex}, sondern eine **systematische Verzerrung** — sie geht immer in dieselbe Richtung, und sie wächst mit der Streuung der Einzelschätzungen.
(b) **Warum 95,5 %.** Damit das Projekt die geplanten 11,33 Tage hält, müssen **alle fünf** Gewerke gleichzeitig ihren Mittelwert unterbieten. Für die Dreiecksverteilung $(6;\ 10;\ 18)$ ist die Wahrscheinlichkeit dafür je Gewerk
@@ -26258,7 +26258,7 @@ Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauer
**1 — (b).** Das kritische Verhältnis $1400/(1400+120) = 0{,}921$ gibt an, wie weit man sich auf die günstigere Fehlerseite stellen soll: Bestellt wird das 92,1-%-Quantil des Bedarfs, hier 38 Stück. (a) ignoriert die Kostenasymmetrie und kostet im Kapitelbeispiel 160 % mehr. (c) verwechselt die Fragestellung: Die Kapitalbindung *ist* bereits in den 120 € je überzähligem Stück enthalten — sie rechtfertigt keine zusätzliche Kürzung.
-**2 — (b).** Der Unterschied liegt darin, **was man wissen muss**: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: Robuste Optimierung`\index{Robuste Optimierung}`{=latex} betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
+**2 — (b).** Der Unterschied liegt darin, **was man wissen muss**: Stochastische Optimierung setzt eine Wahrscheinlichkeitsverteilung voraus und minimiert den Erwartungswert; robuste Optimierung kommt mit einer bloßen Bandbreite aus und sichert den ungünstigsten Fall darin ab. Keines ist „genauer“ (a) — sie beantworten verschiedene Fragen. (c) trifft es nicht: [Robuste Optimierung](#gloss:robuste-optimierung){.glossar-link}`\index{Robuste Optimierung}`{=latex} betrachtet nicht mehr Szenarien, sondern eine ganze Menge auf einmal, und interessiert sich darin nur für den schlechtesten Punkt.
**3 — (c).** Bei durchgehender Linearität und ohne nachgelagerte Entscheidung gilt $\mathbb{E}[f(X)] = f(\mathbb{E}[X])$ — dann ist Rechnen mit Mittelwerten korrekt. Sobald aber ein Maximum, ein Minimum, ein Betrag oder eine Nachbesserungsentscheidung auftaucht, gilt das nicht mehr: Genau das sind die beiden Fälle aus Schnellstart (asymmetrische Kosten über `maximum`) und [Abschnitt 12.9](#sec:unsicherheit-denkfehler) (Maximum über parallele Vorgänge). (a) ist die Fehlannahme, um die es im ganzen Kapitel geht; (b) ist zu absolut — es gibt den linearen Fall, in dem es tatsächlich zulässig ist.
@@ -26277,11 +26277,11 @@ Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauer
**13.1 — Bausteine.** Stufe = Tag 1–5; Zustand = verbleibende Distanz (ggf. plus Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung. **Nicht** in den Zustand gehören: bereits gelaufene Kilometer (redundant, wenn die Restdistanz bekannt ist) und das Wetter von gestern (ohne Einfluss auf die Zukunft).
-**13.2 — Optimalitätsprinzip`\index{Optimalitätsprinzip}`{=latex}.** Weil der Wert eines Zustands nur von den *künftigen* Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von **früheren** Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit **in den Zustand aufnehmen** (dann wächst allerdings der Zustandsraum`\index{Zustandsraum}`{=latex}).
+**13.2 — [Optimalitätsprinzip](#gloss:optimalitaetsprinzip){.glossar-link}`\index{Optimalitätsprinzip}`{=latex}.** Weil der Wert eines Zustands nur von den *künftigen* Entscheidungen abhängt, kann man ihn ab dem Ende rekursiv berechnen. Hängen die Kosten zusätzlich von **früheren** Aktionen ab, ist die Markov-Eigenschaft verletzt — die Lösung: die relevante Vergangenheit **in den Zustand aufnehmen** (dann wächst allerdings der [Zustandsraum](#gloss:zustandsraum){.glossar-link}`\index{Zustandsraum}`{=latex}).
-**13.3 — Rückwärtsinduktion`\index{Rückwärtsinduktion}`{=latex}.** Mit $C(n) = n^2 + 2n$ und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad $2 \to 2 \to 1$ (oder eine Permutation davon) mit Gesamtkosten $8 + 8 + 3 = 19$. Alles auf einmal kostet $25 + 10 = 35$.
+**13.3 — [Rückwärtsinduktion](#gloss:rueckwaertsinduktion){.glossar-link}`\index{Rückwärtsinduktion}`{=latex}.** Mit $C(n) = n^2 + 2n$ und 5 Einheiten in 3 Perioden ergibt sich als optimaler Pfad $2 \to 2 \to 1$ (oder eine Permutation davon) mit Gesamtkosten $8 + 8 + 3 = 19$. Alles auf einmal kostet $25 + 10 = 35$.
-**13.4 — Rucksack als DP.**
+**13.4 — Rucksack als [DP](#gloss:dynamische-programmierung-dp){.glossar-link}.**
```python
V = np.zeros((n + 1, kapazitaet + 1))
for i in range(1, n + 1):
@@ -26329,7 +26329,7 @@ def V(t, lager, lief_vorher):
rest, plan = V(t + 1, neu, p > 0)
```
-Damit findet die Rückwärtsinduktion $(3, 1, 4, 2)$ für 70 € — das nachgerechnete Optimum. Der Zustandsraum **verdoppelt** sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der Fluch der Dimensionalität`\index{Fluch der Dimensionalität}`{=latex}.
+Damit findet die Rückwärtsinduktion $(3, 1, 4, 2)$ für 70 € — das nachgerechnete Optimum. Der Zustandsraum **verdoppelt** sich (je Lagerstand zwei Varianten, „lief“ und „lief nicht“). Das ist der übliche Preis: Vollständigkeit des Zustands kostet Größe, und genau hier beginnt der [Fluch der Dimensionalität](#gloss:fluch-der-dimensionalitaet){.glossar-link}`\index{Fluch der Dimensionalität}`{=latex}.
(d) **Warum der richtige Betrag das Tückische ist.** Man würde erwarten, dass ein falsches Modell auch einen falschen Kostenbetrag ausgibt — dann fiele es beim Nachrechnen auf. Hier nicht: Für den von ihm gewählten Plan $(5,0,5,0)$ stimmt die Rechnung zufällig, weil in diesem Plan tatsächlich jede Produktionsperiode auf eine Pause folgt. Modellannahme und Wirklichkeit fallen für **genau diese eine Lösung** zusammen.
@@ -26350,7 +26350,7 @@ Eine Prüfung, die nur den ausgegebenen Plan nachrechnet, bestätigt also alles.
1. Jeder Teilabschnitt einer optimalen Strategie ist selbst optimal für den Zustand, in dem er beginnt.
2. Weil $V_{t+1}$ bekannt sein muss, um $V_t$ zu berechnen — und am Ende ist der Wert bekannt.
3. Alles, was die Zukunft beeinflusst, und nichts weiter. Unvollständig ist er, wenn die Kosten oder Übergänge zusätzlich von der Vorgeschichte abhängen.
-4. Der Aufwand wächst multiplikativ mit jeder Zustandsdimension. Gegenmittel: gröbere Diskretisierung/Zustandsreduktion, Funktionsapproximation (ADP), Reinforcement Learning`\index{Reinforcement Learning}`{=latex}.
+4. Der Aufwand wächst multiplikativ mit jeder Zustandsdimension. Gegenmittel: gröbere Diskretisierung/Zustandsreduktion, Funktionsapproximation (ADP), [Reinforcement Learning](#gloss:reinforcement-learning){.glossar-link}`\index{Reinforcement Learning}`{=latex}.
5. Weil sie eine **unabhängige** Prüfung ermöglicht — Größenordnungsfehler und Vorzeichenfehler fallen sofort auf.
---
@@ -26371,7 +26371,7 @@ Begründung in einem Satz: *„Für 646 Euro sparen wir 843 Kilogramm CO₂ —
Der Lehrpunkt: **Ein Zielkonflikt ist keine Eigenschaft der Ziele, sondern der Knappheit.** Wer ihn auflösen will, sollte zuerst prüfen, ob sich die Ressource vermehren lässt, statt über Gewichte zu verhandeln.
-**14.4 — Die Front der Relaxation.** Ohne `integrality` ist der zulässige Bereich konvex, und die Pareto-Front`\index{Pareto-Front}`{=latex} liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also **keinen** Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
+**14.4 — Die Front der Relaxation.** Ohne `integrality` ist der zulässige Bereich konvex, und die [Pareto-Front](#gloss:pareto-front){.glossar-link}`\index{Pareto-Front}`{=latex} liegt vollständig auf ihrer eigenen unteren konvexen Hülle. Es gibt also **keinen** Punkt oberhalb — jeder Punkt der Front ist durch ein passendes Gewicht erreichbar.
Genau das ist der Grund, warum das Problem in Lehrbüchern zur linearen Programmierung nicht vorkommt und in der betrieblichen Praxis ständig: Sobald „welcher Träger“, „welches Lager“ oder „welche Schicht“ entschieden wird, zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen die Einbuchtungen, in denen die nicht gestützten Lösungen liegen.
@@ -26423,7 +26423,7 @@ Das ist die richtige Richtung und leicht zu merken: Je billiger der Überhang, d
Beide Bedingungen sind unrealistisch. Fehlmenge und Überhang kosten fast nie dasselbe — bei Frischware ist der Überhang teurer, bei Ersatzteilen die Fehlmenge um Größenordnungen. Und Nachfrageverteilungen sind meist rechtsschief. Der Mittelwert ist damit die Ausnahme, nicht die Regel — er wird nur benutzt, weil Regressionsmodelle ihn standardmäßig liefern.
-**15.3 — Die Kennzahl der Prognoseabteilung.** Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim MAPE, weil beide Maße den *Erwartungswert* belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
+**15.3 — Die Kennzahl der Prognoseabteilung.** Die Rangfolge bleibt: Die Punktprognose gewinnt auch beim [MAPE](#gloss:mape){.glossar-link}, weil beide Maße den *Erwartungswert* belohnen — der MAPE etwas anders gewichtet, aber ebenfalls symmetrisch in dem Sinne, dass er nicht weiß, dass Unterschätzung teurer ist als Überschätzung.
Das ist kein Zufall, sondern der Kern des Kapitels: **Jedes rein statistische Fehlermaß ignoriert die Kostenasymmetrie.** Man kann das Problem nicht lösen, indem man das Fehlermaß wechselt; man muss die Kosten selbst messen. (Die einzige Ausnahme ist der *Pinball Loss* — genau das Maß, das die Quantilregression minimiert, und das ist eben kein allgemeines Prognosemaß, sondern eines für ein bestimmtes Quantil.)
@@ -26431,7 +26431,7 @@ Das ist kein Zufall, sondern der Kern des Kapitels: **Jedes rein statistische Fe
Ein praktischer Nebeneffekt: Ein solches Band ist die verständlichste Form, Unsicherheit an die Disposition zu berichten. „Zwischen 130 und 210" sagt einem Menschen mehr als „170 ± Sicherheitszuschlag".
-**15.5 — Der Wert der Merkmale.** Zu erwarten ist, dass die **Kosten stärker steigen als der MSE**. Der Grund: Das Merkmal „Aktion" trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) *und* die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
+**15.5 — Der Wert der Merkmale.** Zu erwarten ist, dass die **Kosten stärker steigen als der [MSE](#gloss:mse){.glossar-link}**. Der Grund: Das Merkmal „Aktion" trägt zwei verschiedene Informationen — es verschiebt den Erwartungswert (um 45 Stück) *und* die Streuung (von 8 auf 30). Der MSE bemerkt nur den ersten Teil; die Entscheidungskosten spüren beide.
Der Lehrsatz dahinter: **Der Wert eines Merkmals hängt davon ab, wofür man es benutzt.** Eine Merkmalsauswahl, die nach MSE-Beitrag sortiert, wirft möglicherweise genau die Merkmale weg, die für die Entscheidung am wichtigsten sind — nämlich die, die etwas über die Unsicherheit sagen.
@@ -26488,7 +26488,7 @@ Bei 40 Lieferanten hat die Kovarianzmatrix 820 zu schätzende Einträge. Wer daf
Benötigt würden: Lieferzeit-Zeitreihen je Lieferant über denselben Zeitraum, gleich getaktet. Genau daran scheitert es in der Praxis meist — die Daten liegen in Bestellvorgängen, nicht in einer Matrix.
-**16.4 — CVaR mit Ganzzahligkeit.** Man ergänzt Binärvariablen $y_j$ je Lieferant und die Kopplung $w_j \le y_j$ sowie $\sum_j y_j \le 3$. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt `cp.Variable(n, boolean=True)`.
+**16.4 — [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link} mit Ganzzahligkeit.** Man ergänzt Binärvariablen $y_j$ je Lieferant und die Kopplung $w_j \le y_j$ sowie $\sum_j y_j \le 3$. Da CVXPY gemischt-ganzzahlige Probleme unterstützt, genügt `cp.Variable(n, boolean=True)`.
Zu erwarten ist ein **höherer** CVaR: Der zulässige Bereich wird kleiner, und die Feinabstimmung über sechs Lieferanten fällt weg. Der Aufschlag ist der Preis der Vertragswirklichkeit — und genau die Zahl, die man dem Einkauf vorlegt, wenn dort jemand sagt, drei Lieferanten seien genug.
@@ -26519,7 +26519,7 @@ Markowitz liefert stetige Anteile: 7,3 % bei Lieferant A, 4,1 % bei B. Ein Liefe
### Selbsttest {#selbsttest-loesung-bruecke}
1. Montagestunden ↔ Kapital; Plattenmaterial ↔ Risikobudget; Deckungsbeitrag je Stück ↔ erwarteter Ertrag je 1 000 €. (Weitere: Mindestlosgröße ↔ Mindestordergröße, Rüstkosten ↔ Ordergebühr, Sortimentsbreite ↔ Kardinalitätsgrenze.)
-2. Ein LP sieht eine Matrix, einen Kapazitätsvektor`\index{Kapazitätsvektor}`{=latex} und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
+2. Ein LP sieht eine Matrix, einen [Kapazitätsvektor](#gloss:kapazitaetsvektor){.glossar-link}`\index{Kapazitätsvektor}`{=latex} und einen Zielvektor — Bedeutung kommt darin nicht vor. Das ist hier ein Vorteil, weil derselbe geprüfte, getestete Code beide Domänen bedient; man erbt die Verlässlichkeit mit.
3. „Stellen Sie sich vor, Ihre Engpassmaschine wäre nicht die Fräse, sondern eine Vorschrift: Sie dürfen nur eine bestimmte Menge Risiko in den Büchern haben. Der Schattenpreis sagt dann, was eine Lockerung dieser Vorschrift wert wäre — genau wie bei einer zusätzlichen Maschinenstunde."
4. **Fette Ränder** (seltene, aber sehr große Abweichungen). Die Standardabweichung mittelt sie weg und bestraft außerdem Abweichungen nach oben genauso wie nach unten; CVaR sieht ausschließlich auf den schlechten Rand.
5. (a) Sind die Zahlen gemessen oder geschätzt — und wie groß ist der Schätzfehler im Verhältnis zu den Unterschieden? (b) Ist der datengenerierende Prozess stabil, oder reagiert er auf das Modell? (c) Sind die Entscheidungen teilbar oder ganzzahlig?
@@ -26649,7 +26649,7 @@ Und immer: **außerhalb des Schätzzeitraums prüfen.** Ein Modell, das nur im e
### Selbsttest {#selbsttest-loesung-finanzdaten}
1. Log-Renditen sind Differenzen von Logarithmen — die addieren sich über die Zeit. Diskrete Renditen sind lineare Anteile am Kapital — die addieren sich über gewichtete Positionen.
2. Die Optimierung sucht die Richtungen kleinster geschätzter Varianz — genau jene, deren Eigenwerte am stärksten nach unten verzerrt sind. Sie optimiert dadurch in das Schätzrauschen hinein.
-3. Der Rang von $\mathbf{X}^\top\mathbf{X}$ ist höchstens $T < N$ — die Matrix ist nicht invertierbar, das GMV-Problem hat unendlich viele Lösungen.
+3. Der Rang von $\mathbf{X}^\top\mathbf{X}$ ist höchstens $T < N$ — die Matrix ist nicht invertierbar, das [GMV](#gloss:global-minimum-variance-portfolio-gmv){.glossar-link}-Problem hat unendlich viele Lösungen.
4. Stichprobenmatrix (unverzerrt, verrauscht) mit strukturiertem Ziel (verzerrt, stabil). $\delta$ wird analytisch so bestimmt, dass der erwartete quadratische Fehler minimal wird.
5. Skalierte Einheitsmatrix (sklearn) und Konstant-Korrelations-Ziel (LW 2003).
@@ -26669,11 +26669,11 @@ Und immer: **außerhalb des Schätzzeitraums prüfen.** Ein Modell, das nur im e
Rechenweg für $(0{,}5;\ 0{,}5)$: $\sigma_p^2 = 0{,}25\cdot0{,}0625 + 0{,}25\cdot0{,}0144
+ 2\cdot0{,}25\cdot(-0{,}2)\cdot0{,}25\cdot0{,}12 = 0{,}015625 + 0{,}0036 - 0{,}003 = 0{,}016225$, also $\sigma_p = 12{,}74\,\%$.
-Beste Sharpe Ratio`\index{Sharpe-Ratio}`{=latex} unter den vier Kandidaten: $(0{,}3;\ 0{,}7)$. Das exakte Optimum liegt bei $w_1 = 0{,}318$. **Bemerkenswert:** Die Mischung $(0{,}3;\ 0{,}7)$ hat mit 10,08 % eine **geringere** Volatilität`\index{Volatilität}`{=latex} als *beide* Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
+Beste [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}`\index{Sharpe-Ratio}`{=latex} unter den vier Kandidaten: $(0{,}3;\ 0{,}7)$. Das exakte Optimum liegt bei $w_1 = 0{,}318$. **Bemerkenswert:** Die Mischung $(0{,}3;\ 0{,}7)$ hat mit 10,08 % eine **geringere** [Volatilität](#gloss:volatilitaet){.glossar-link}`\index{Volatilität}`{=latex} als *beide* Einzeltitel (25 % und 12 %) — genau der Diversifikationseffekt, den die negative Korrelation ermöglicht.
**19.2 — Lambda deuten.** Von $\lambda=0$ (GMV, linker unterer Punkt der Kurve) wandert die Lösung entlang der Effizienzgrenze nach rechts oben, bis sie bei $\lambda\to\infty$ im Titel mit der höchsten Rendite endet (bzw. an der Positionsobergrenze).
-**19.3 — Korn-Transformation`\index{Korn-Transformation}`{=latex}.** $\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}}$ — bei einem Portfolio mit $\sum w_i = 1$ und Überrenditen geschrieben als $\mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1})$ kürzt sich $c$ heraus. Mit der Bedingung $\sum w_i = 1$ ist die Skala jedoch **fixiert**, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch $\sum y_i = \kappa$ mit freiem $\kappa$ und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
+**19.3 — [Korn-Transformation](#gloss:korn-transformation){.glossar-link}`\index{Korn-Transformation}`{=latex}.** $\text{SR}(c\mathbf{w}) = \frac{c\mathbf{w}^\top\boldsymbol{\mu}-r_f}{\sqrt{c^2\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}}$ — bei einem Portfolio mit $\sum w_i = 1$ und Überrenditen geschrieben als $\mathbf{w}^\top(\boldsymbol{\mu}-r_f\mathbf{1})$ kürzt sich $c$ heraus. Mit der Bedingung $\sum w_i = 1$ ist die Skala jedoch **fixiert**, man kann also nicht frei skalieren. Die Transformation ersetzt diese Bedingung durch $\sum y_i = \kappa$ mit freiem $\kappa$ und normiert stattdessen die Überrendite auf 1 — dadurch wird die Skala wieder frei und das Problem konvex.
**19.4 — Restriktionen kosten.** Erwartetes Muster: Sharpe Ratio sinkt monoton mit strengerer Grenze. Unlösbar wird es bei $w_{\max} < 1/n$ — dann kann die Summe der Gewichte 1 nicht mehr erreicht werden.
@@ -26721,7 +26721,7 @@ Zusammen ergibt das das beobachtete Bild: 38 % Konzentration in einer Anlage, ob
### Micro-Quiz {#quiz-loesung-markowitz}
-**1 — (b).** Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — Diversifikation`\index{Diversifikation}`{=latex} findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
+**1 — (b).** Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und besteht vollständig aus der renditestärksten Einzelanlage — [Diversifikation](#gloss:diversifikation){.glossar-link}`\index{Diversifikation}`{=latex} findet dort gar nicht mehr statt. (a) und (c) sind sachlich falsch: Der Punkt ist numerisch völlig unproblematisch und wird von CVXPY ohne Weiteres gefunden.
**2 — (b) es braucht keine Renditeschätzung.** Renditen sind die mit Abstand unzuverlässigste Eingangsgröße (Standardfehler größer als der geschätzte Wert selbst); wer sie nicht benötigt, umgeht das Problem vollständig. (a) trifft nicht zu — die Nebenbedingungen sind dieselben. (c) ist falsch: Das Minimum-Varianz-Portfolio erzielt *erwartungsgemäß* weniger Rendite; sein Vorteil liegt in der Verlässlichkeit, nicht in der Höhe.
@@ -26738,9 +26738,9 @@ Zusammen ergibt das das beobachtete Bild: 38 % Konzentration in einer Anlage, ob
## A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten`\index{Transaktionskosten}`{=latex}“ {#sec:loesungen-cvar}
-**20.1 — VaR und CVaR von Hand.** Sortierte Verluste: $-2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9$. Das 80 %-Quantil ist der 8. Wert: $\text{VaR}_{80\%} = 2{,}1$. $\text{CVaR}_{80\%} = $ Mittel der schlechtesten 20 % $= (5{,}4+8{,}9)/2 = \mathbf{7{,}15}$.
+**20.1 — [VaR](#gloss:value-at-risk-var){.glossar-link} und CVaR von Hand.** Sortierte Verluste: $-2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9$. Das 80 %-Quantil ist der 8. Wert: $\text{VaR}_{80\%} = 2{,}1$. $\text{CVaR}_{80\%} = $ Mittel der schlechtesten 20 % $= (5{,}4+8{,}9)/2 = \mathbf{7{,}15}$.
-**20.2 — Subadditivität`\index{Subadditivität}`{=latex}.** Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
+**20.2 — [Subadditivität](#gloss:subadditivitaet){.glossar-link}`\index{Subadditivität}`{=latex}.** Ein Risikomaß sollte Diversifikation nie bestrafen: Das Risiko eines zusammengelegten Portfolios darf nicht größer sein als die Summe der Einzelrisiken. Wird das verletzt, hätte eine Bank einen Anreiz, Portfolios künstlich aufzuspalten, um Eigenkapitalanforderungen zu senken — ökonomisch unsinnig.
**20.3 — Rockafellar-Uryasev nachvollziehen.** (a) Schlechtestes Drittel von $(1,4,9)$ ist $\{9\}$ → CVaR $= 9$. (b) Mit $\frac{1}{S(1-\alpha)} = \frac{1}{3\cdot(1/3)} = 1$: $\gamma=0$: $0 + (1+4+9) = 14$. $\gamma=1$: $1 + (0+3+8) = 12$. $\gamma=4$: $4 + (0+0+5) = \mathbf{9}$. $\gamma=5$: $5+4 = 9$. $\gamma=9$: $9+0 = 9$. (c) Minimum ab $\gamma = 4$ bei **9** — identisch mit (a) ✓. (Das Minimum wird auf einem ganzen Intervall angenommen, weil die Funktion stückweise linear ist — genau der Grund, warum der CVaR nicht *streng* konvex ist.)
@@ -26782,7 +26782,7 @@ Auf Deutsch: Ein Portfolio darf nie riskanter sein als seine Teile zusammen; Div
Formal folgt die Subadditivität daraus, dass der CVaR sich als **Maximum über Erwartungswerte** schreiben lässt (Darstellungssatz für kohärente Risikomaße), und Maxima von Erwartungswerten sind stets subadditiv. Anschaulicher: Ein Mittelwert über eine Menge verhält sich gutartig, wenn man Mengen zusammenlegt; ein Quantil nicht — es kann springen, sobald sich die Reihenfolge der Szenarien ändert.
-**Die praktische Konsequenz** ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den *Expected Shortfall*`\index{Expected Shortfall}`{=latex} — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
+**Die praktische Konsequenz** ist die Umstellung der Bankenaufsicht mit Basel III vom VaR auf den *[Expected Shortfall](#gloss:expected-shortfall){.glossar-link}*`\index{Expected Shortfall}`{=latex} — dieselbe Größe, die hier CVaR heißt. Und für Ihre eigenen Modelle, auch außerhalb der Finanzwelt: Wo immer Sie eine Risikokennzahl über Einheiten aggregieren wollen, prüfen Sie zuerst, ob das Maß das überhaupt zulässt.
### Micro-Quiz {#quiz-loesung-cvar}
@@ -26803,21 +26803,21 @@ Formal folgt die Subadditivität daraus, dass der CVaR sich als **Maximum über
## A.21 Lösungen zu Kapitel „Die vollständige quantitative Handelsmaschine“ {#sec:loesungen-handelsmaschine}
-**21.1 — Lookahead erkennen.** (a) sauber (`:heute`). (b) **Lookahead** — gesamter Zeitraum. (c) **Lookahead** — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) **Survivorship-Bias**`\index{Survivorship-Bias}`{=latex} — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
+**21.1 — Lookahead erkennen.** (a) sauber (`:heute`). (b) **Lookahead** — gesamter Zeitraum. (c) **Lookahead** — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus Daten bis heute, angewendet auf heute (idealerweise auf morgen). (e) **[Survivorship-Bias](#gloss:survivorship-bias){.glossar-link}**`\index{Survivorship-Bias}`{=latex} — das Universum wird danach gefiltert, wer über den gesamten Zeitraum Daten hat.
**21.2 — Kennzahlen deuten.** A: Sharpe $= (12-2)/22 = 0{,}45$, Calmar $= 12/35 = 0{,}34$. B: Sharpe $= (8-2)/9 = 0{,}67$, Calmar $= 8/12 = 0{,}67$. **Empfehlung: B** — für einen Pensionsfonds ist der Drawdown entscheidend, weil laufende Auszahlungen in einer Verlustphase Substanz vernichten. B ist in beiden risikoadjustierten Maßen besser.
-**21.3 — Rebalancing`\index{Rebalancing}`{=latex}-Kalender.** Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
+**21.3 — [Rebalancing](#gloss:rebalancing){.glossar-link}`\index{Rebalancing}`{=latex}-Kalender.** Etwa 25–30 % der Termine fallen aus. Die Kennzahlen ändern sich messbar — in welche Richtung, ist zufällig. Genau das ist der Punkt: Der Fehler verzerrt, ohne aufzufallen.
**21.4 — Rebalancing-Frequenz.** Typisches Muster: Turnover und Kosten steigen etwa linear mit der Frequenz, der Bruttoertrag verbessert sich nur unterproportional. Bei 0,15 % Gebühren ist monatlich meist vertretbar, bei 0,5 % eher quartalsweise. Ein **Toleranzband** (nur handeln bei Abweichung > x %) schlägt fast immer die feste Frequenz.
**21.5 — Krisenverhalten.** Erwartung: Die Überrendite ist selten stabil; oft stammt sie aus wenigen Perioden. Folgerung: Eine gute Gesamtkennzahl kann von einer einzigen glücklichen Phase getragen sein — **immer** nach Teilzeiträumen aufschlüsseln.
-**21.6 — Data Snooping`\index{Data Snooping}`{=latex} messen.** Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die **Differenz zwischen (a) und (c) ist der Selektionseffekt** — genau das, was die Deflated Sharpe Ratio korrigieren soll.
+**21.6 — [Data Snooping](#gloss:data-snooping){.glossar-link}`\index{Data Snooping}`{=latex} messen.** Erwartetes Ergebnis: Die beste In-Sample-Sharpe-Ratio liegt deutlich über dem Mittelwert aller Varianten; auf dem zurückgehaltenen Zeitraum fällt sie Richtung Mittelwert zurück. Die **Differenz zwischen (a) und (c) ist der Selektionseffekt** — genau das, was die Deflated Sharpe Ratio korrigieren soll.
### Finde den Denkfehler — Die Strategie mit dem hochsignifikanten Ergebnis
-(a) **Warum ein korrekter Backtest wertlos sein kann.** Der Backtest misst genau das, was er messen soll: die Wertentwicklung *dieser einen* Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: *„Ist dieses Ergebnis besser, als es Zufall erklären kann?“*
+(a) **Warum ein korrekter [Backtest](#gloss:backtest){.glossar-link} wertlos sein kann.** Der Backtest misst genau das, was er messen soll: die Wertentwicklung *dieser einen* Strategie. Die Frage, die beantwortet werden soll, lautet aber anders — nämlich: *„Ist dieses Ergebnis besser, als es Zufall erklären kann?“*
Und für diese Frage ist entscheidend, dass die Strategie als **Beste aus hundert** ausgewählt wurde. Ein p-Wert von 0,0094 bedeutet: „Wenn diese Strategie keinen Vorteil hätte, sähe sie in 0,94 % der Fälle so gut aus.“ Bei hundert Versuchen erwartet man aber rund fünf Ergebnisse unter dem 5-%-Niveau — allein durch Zufall. Genau das zeigt die Tabelle: Auf reinem Rauschen liefert die Beste von hundert im Mittel Sharpe 1,13 und p = 0,0094. **Dieselben Zahlen.**
@@ -26919,7 +26919,7 @@ Das ist das eigentliche Argument für die Überwachung: Nicht, dass ein Gap von
**2 — (b) aus dem ausgegebenen Tourenplan.** Eine Prüfung muss von der **Lösung** ausgehen und die Anforderungen unabhängig nachrechnen. (a) fragt die Ladungsdimension — also ausgerechnet den Baustein, dessen Fehlen der Fehler war (siehe [Abschnitt 8.7](#sec:graphen-denkfehler)); existiert sie nicht, stürzt die Prüfung ab, existiert sie, kann sie per Konstruktion nie verletzt sein. (c) prüft gar nichts: Ob eine Kapazität in der Zielfunktion bepreist ist, sagt nichts darüber, ob sie eingehalten wurde.
-**3 — (c) hierarchisch lockern.** `INFEASIBLE` sagt nur, *dass* ein Widerspruch existiert, nicht *welche* Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der Kostenzerlegung`\index{Kostenzerlegung}`{=latex}. Genau das tut `Infeasibility_Diagnose.py`. (a) hilft nicht: `INFEASIBLE` ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
+**3 — (c) hierarchisch lockern.** `INFEASIBLE` sagt nur, *dass* ein Widerspruch existiert, nicht *welche* Regeln ihn erzeugen. Ersetzt man harte Verbote durch sehr teure Strafkosten, liefert der Solver wieder eine Lösung — und die verletzten Regeln stehen benannt in der [Kostenzerlegung](#gloss:kostenzerlegung){.glossar-link}`\index{Kostenzerlegung}`{=latex}. Genau das tut `Infeasibility_Diagnose.py`. (a) hilft nicht: `INFEASIBLE` ist ein Beweis, kein Abbruch. (b) verschiebt das Problem und verliert die Gelegenheit, die Ursache zu finden, solange sie noch frisch ist.
### Selbsttest {#selbsttest-loesung-praxisfallen}
1. Durch hierarchische Relaxation: Schlupfvariablen mit hohen, gestaffelten Strafkosten für die verletzbaren Bedingungen.
@@ -26967,7 +26967,7 @@ Die erste wird getötet (`test_pruefung_findet_kapazitaetsverletzung`). Die zwei
**23.4 — Der Benchmark mit MILP.** Zu erwarten ist, dass sich die Reihenfolge ändert. Beim reinen LP entscheidet vor allem der Modellaufbau in Python; beim MILP verschiebt sich das Gewicht zum Lösen, und dort spielen die Branch-and-Bound-Heuristiken der Bibliotheken gegeneinander. Die Spalte „Anteil" sollte bei allen deutlich **fallen** — nicht weil der Aufbau schneller würde, sondern weil das Lösen langsamer wird. Genau deshalb steht im Kapitel, dass die Tabelle nichts über MILPs sagt.
-**23.5 — Der Dienst mit Zeitlimit.** Der Modellbauer in `or_kern.py` nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei GLOP `solver.SetTimeLimit(millisekunden)`). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver `ZEITLIMIT`, ist `stand` weiterhin `gescheitert`, aber mit einer anderen Begründung als bei `UNZULAESSIG` — der Unterschied zwischen „rechne länger" und „ändere das Modell" ([Kapitel 6](#kap-milp)).
+**23.5 — Der Dienst mit Zeitlimit.** Der Modellbauer in `or_kern.py` nimmt bisher kein Zeitlimit entgegen — das ist der erste Schritt (bei [GLOP](#gloss:glop){.glossar-link} `solver.SetTimeLimit(millisekunden)`). Danach im Arbeiter durchreichen und den Status auswerten: Liefert der Solver `ZEITLIMIT`, ist `stand` weiterhin `gescheitert`, aber mit einer anderen Begründung als bei `UNZULAESSIG` — der Unterschied zwischen „rechne länger" und „ändere das Modell" ([Kapitel 6](#kap-milp)).
Für den Test braucht es eine Instanz, die das Limit reißt. Ein LP eignet sich schlecht dafür; nehmen Sie ein MILP mit einigen hundert Binärvariablen und ein Limit von 0,1 Sekunden.
@@ -27002,7 +27002,7 @@ Was geholfen hätte — drei Dinge, keines davon ein Unit-Test:
3. „Grün" heißt nur, dass kein Test fehlgeschlagen ist — das gilt auch für eine Suite aus lauter `assert True`. „Prüft etwas" heißt, dass die Tests bei einem eingebauten Fehler rot würden; genau das misst der Mutationstest.
4. Weil sonst nicht erkennbar ist, welche Hälfte die Zeit kostet. Im Kapitel gehen bei OR-Tools 78 % in den Aufbau — wer nur die Gesamtzeit sieht, wechselt den Solver und ändert damit fast nichts.
5. Weil die Rechnung Sekunden bis Minuten dauert. Eine synchrone Antwort läuft in den Timeout des Reverse Proxy und blockiert währenddessen einen Arbeiter. `202` heißt „angenommen, noch nicht fertig" — genau die richtige Aussage.
-6. Ein Threadpool genügt, wenn der Solver den GIL freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während `Solve()`, im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
+6. Ein Threadpool genügt, wenn der Solver den [GIL](#gloss:gil-global-interpreter-lock){.glossar-link} freigibt — das tun die C++-Bibliotheken (OR-Tools, HiGHS) während `Solve()`, im Kapitel mit Faktor 3,5 bei vier Threads gemessen. Eine in reinem Python geschriebene Heuristik hält den GIL; für sie braucht es Prozesse.
---
@@ -27020,37 +27020,37 @@ Was geholfen hätte — drei Dinge, keines davon ein Unit-Test:
| # | Muster | Umgangssprachlich | Wo im Buch |
| --- | --- | --- | --- |
| **Logische Schalter** |
-| B1 | Aktivierungsschalter`\index{Aktivierungsschalter (Modellierungsmuster)}`{=latex} | „Wenn genutzt, dann Fixkosten“ | [Kapitel 6](#kap-milp) |
+| B1 | [Aktivierungsschalter](#gloss:aktivierungsschalter){.glossar-link}`\index{Aktivierungsschalter (Modellierungsmuster)}`{=latex} | „Wenn genutzt, dann Fixkosten“ | [Kapitel 6](#kap-milp) |
| B2 | Semikontinuierlich | „Entweder 0 oder mindestens L“ | [Kapitel 6](#kap-milp) |
-| B3 | Implikation`\index{Implikation (Modellierungsmuster)}`{=latex} | „Wenn A, dann auch B“ | [Kapitel 6](#kap-milp) |
+| B3 | [Implikation](#gloss:implikation){.glossar-link}`\index{Implikation (Modellierungsmuster)}`{=latex} | „Wenn A, dann auch B“ | [Kapitel 6](#kap-milp) |
| B4 | Entweder-Oder | „A oder B, aber nicht beides“ | [Kapitel 6](#kap-milp) |
-| B5 | Exklusiv-Oder`\index{Exklusiv-Oder}`{=latex} | „Genau eines von N“ | [Kapitel 7](#kap-cpsat) |
+| B5 | [Exklusiv-Oder](#gloss:exklusiv-oder){.glossar-link}`\index{Exklusiv-Oder}`{=latex} | „Genau eines von N“ | [Kapitel 7](#kap-cpsat) |
| B6 | Kardinalität | „Höchstens K von N“ | [Kapitel 6](#kap-milp) |
-| B7 | Bedingte Kopplung`\index{Bedingte Kopplung (Konjunktion)}`{=latex} | „Wenn A und B, dann C“ | [Kapitel 6](#kap-milp) |
+| B7 | [Bedingte Kopplung](#gloss:bedingte-kopplung){.glossar-link}`\index{Bedingte Kopplung (Konjunktion)}`{=latex} | „Wenn A und B, dann C“ | [Kapitel 6](#kap-milp) |
| **Mengen und Grenzen** |
| B8 | Weiche Grenze | „Möglichst nicht über X“ | [Kapitel 7](#kap-cpsat) |
-| B9 | Gestaffelte Preise`\index{Gestaffelte Preise (stückweise linear)}`{=latex} | „Erste 100 Stück billiger“ | — |
-| B10 | Absolutbetrag`\index{Absolutbetrag (Modellierungsmuster)}`{=latex} | „Abweichung nach oben wie unten“ | [Kapitel 20](#kap-cvar) |
-| B11 | Min/Max in der Zielfunktion`\index{Zielfunktion}`{=latex} | „Den Schlechtesten verbessern“ | [Kapitel 7](#kap-cpsat) |
-| B12 | Verhältnis-Bedingung`\index{Verhältnis-Bedingung}`{=latex} | „Anteil mindestens 30 %“ | [Kapitel 19](#kap-markowitz) |
+| B9 | [Gestaffelte Preise](#gloss:gestaffelte-preise){.glossar-link}`\index{Gestaffelte Preise (stückweise linear)}`{=latex} | „Erste 100 Stück billiger“ | — |
+| B10 | [Absolutbetrag](#gloss:absolutbetrag){.glossar-link}`\index{Absolutbetrag (Modellierungsmuster)}`{=latex} | „Abweichung nach oben wie unten“ | [Kapitel 20](#kap-cvar) |
+| B11 | Min/Max in der [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex} | „Den Schlechtesten verbessern“ | [Kapitel 7](#kap-cpsat) |
+| B12 | [Verhältnis-Bedingung](#gloss:verhaeltnis-bedingung){.glossar-link}`\index{Verhältnis-Bedingung}`{=latex} | „Anteil mindestens 30 %“ | [Kapitel 19](#kap-markowitz) |
| B25 | Mindestabnahme im Zeitraum | „Entweder gar nicht oder 500 im Jahr“ | [Kapitel 16](#kap-bruecke) |
-| B27 | Budgetlimit`\index{Budgetlimit}`{=latex} | „Mehr als 2 Mio. gibt es nicht“ | [Kapitel 6](#kap-milp) |
+| B27 | [Budgetlimit](#gloss:budgetlimit){.glossar-link}`\index{Budgetlimit}`{=latex} | „Mehr als 2 Mio. gibt es nicht“ | [Kapitel 6](#kap-milp) |
| **Zeit und Reihenfolge** |
-| B13 | Vorrangbeziehung`\index{Vorrangbeziehung}`{=latex} | „B erst nach A“ | [Kapitel 7](#kap-cpsat) |
-| B14 | Nichtüberlappung`\index{Nichtüberlappung}`{=latex} | „Eine Maschine, ein Job“ | [Kapitel 7](#kap-cpsat) |
-| B15 | Kumulative Ressource`\index{Kumulative Ressource}`{=latex} | „Höchstens 3 gleichzeitig“ | [Kapitel 7](#kap-cpsat) |
-| B16 | Gleitendes Fenster`\index{Gleitendes Fenster}`{=latex} | „Höchstens 5 Tage in Folge“ | [Kapitel 7](#kap-cpsat) |
-| B17 | Umrüstkosten`\index{Umrüstkosten (Modellierungsmuster)}`{=latex} | „Wechsel kostet extra“ | [Kapitel 7](#kap-cpsat) |
-| B26 | Rüstzeit`\index{Rüstzeit}`{=latex} als Kapazität | „Umbauen kostet Maschinenstunden“ | [Kapitel 17](#kap-supplychain) |
+| B13 | [Vorrangbeziehung](#gloss:vorrangbeziehung){.glossar-link}`\index{Vorrangbeziehung}`{=latex} | „B erst nach A“ | [Kapitel 7](#kap-cpsat) |
+| B14 | [Nichtüberlappung](#gloss:nichtueberlappung){.glossar-link}`\index{Nichtüberlappung}`{=latex} | „Eine Maschine, ein Job“ | [Kapitel 7](#kap-cpsat) |
+| B15 | [Kumulative Ressource](#gloss:kumulative-ressource){.glossar-link}`\index{Kumulative Ressource}`{=latex} | „Höchstens 3 gleichzeitig“ | [Kapitel 7](#kap-cpsat) |
+| B16 | [Gleitendes Fenster](#gloss:gleitendes-fenster){.glossar-link}`\index{Gleitendes Fenster}`{=latex} | „Höchstens 5 Tage in Folge“ | [Kapitel 7](#kap-cpsat) |
+| B17 | [Umrüstkosten](#gloss:umruestkosten){.glossar-link}`\index{Umrüstkosten (Modellierungsmuster)}`{=latex} | „Wechsel kostet extra“ | [Kapitel 7](#kap-cpsat) |
+| B26 | [Rüstzeit](#gloss:ruestzeit){.glossar-link}`\index{Rüstzeit}`{=latex} als Kapazität | „Umbauen kostet Maschinenstunden“ | [Kapitel 17](#kap-supplychain) |
| **Robustheit und Diagnose** |
| B18 | Schlupf gegen Unlösbarkeit`\index{Schlupfvariable!gegen Unlösbarkeit}`{=latex} | „Regel notfalls brechen“ | [Kapitel 22](#kap-praxisfallen) |
| B19 | Hierarchische Ziele | „Erst A, dann B optimieren“ | [Kapitel 22](#kap-praxisfallen) |
-| B20 | Symmetriebrechung`\index{Symmetriebrechung}`{=latex} | „Gleiche Objekte nicht doppelt zählen“ | [Kapitel 10](#kap-dekomposition) |
-| B21 | Worst-Case-Abzug`\index{Robuste Optimierung!Worst-Case-Abzug}`{=latex} | „Gegen Schätzfehler`\index{Schätzfehler}`{=latex} absichern“ | [Kapitel 12](#kap-unsicherheit) |
+| B20 | [Symmetriebrechung](#gloss:symmetriebrechung){.glossar-link}`\index{Symmetriebrechung}`{=latex} | „Gleiche Objekte nicht doppelt zählen“ | [Kapitel 10](#kap-dekomposition) |
+| B21 | Worst-Case-Abzug`\index{Robuste Optimierung!Worst-Case-Abzug}`{=latex} | „Gegen [Schätzfehler](#gloss:schaetzfehler){.glossar-link}`\index{Schätzfehler}`{=latex} absichern“ | [Kapitel 12](#kap-unsicherheit) |
| **Netzwerke** |
-| B22 | Flusserhaltung`\index{Flusserhaltung}`{=latex} | „Was reinkommt, geht raus“ | [Kapitel 8](#kap-graphen) |
+| B22 | [Flusserhaltung](#gloss:flusserhaltung){.glossar-link}`\index{Flusserhaltung}`{=latex} | „Was reinkommt, geht raus“ | [Kapitel 8](#kap-graphen) |
| B23 | Zuordnung 1:1 | „Jeder genau eine Aufgabe“ | [Kapitel 8](#kap-graphen) |
-| B24 | Subtour`\index{Subtour}`{=latex}-Eliminierung`\index{Subtour!Eliminierung}`{=latex} | „Keine isolierten Kreise“ | [Kapitel 8](#kap-graphen) |
+| B24 | [Subtour](#gloss:subtour){.glossar-link}`\index{Subtour}`{=latex}-Eliminierung`\index{Subtour!Eliminierung}`{=latex} | „Keine isolierten Kreise“ | [Kapitel 8](#kap-graphen) |
> **Umgekehrte Richtung:** Wer von einem Satz aus der Besprechung kommt und das Muster sucht, findet in [Abschnitt 4.6](#sec:modellierung-glossar) ein Lexikon, das genau so herum aufgebaut ist — und die Wendungen benennt, bei denen es keine eindeutige Übersetzung gibt.
>
@@ -27071,7 +27071,7 @@ modell.Add(x <= M * y) # CP-SAT
# LP/MILP: Zeile x - M*y <= 0
```
-> ⚠️ **$M$ so klein wie möglich** — idealerweise die ohnehin vorhandene Kapazitätsgrenze von $x$. Zu großes $M$ macht die LP-Relaxation`\index{LP-Relaxation}`{=latex} wertlos ([Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)).
+> ⚠️ **$M$ so klein wie möglich** — idealerweise die ohnehin vorhandene Kapazitätsgrenze von $x$. Zu großes $M$ macht die [LP-Relaxation](#gloss:lp-relaxation){.glossar-link}`\index{LP-Relaxation}`{=latex} wertlos ([Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)).
### B2 — Semikontinuierliche Variable
@@ -27203,7 +27203,7 @@ for s in lieferanten:
> ⚠️ **Die häufigste Verwechslung:** $L\,y_t \le x_t$ **je Periode** (B2) ist ein *anderes* Modell — es verlangt in jeder einzelnen Periode eine Mindestmenge und ist erheblich strenger. Wer den Jahresvertrag so formuliert, erzeugt ein unlösbares Modell und sucht den Fehler dann in den Daten.
-> ⚠️ **Die obere Kopplung nicht vergessen.** Ohne $\sum_t x_t \le U\,y$ kann das Modell $y = 0$ setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der *Trickle Flow*`\index{Trickle Flow}`{=latex} aus [Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen), nur andersherum. $U$ ist die Jahreskapazität des Lieferanten, keine runde Zahl.
+> ⚠️ **Die obere Kopplung nicht vergessen.** Ohne $\sum_t x_t \le U\,y$ kann das Modell $y = 0$ setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der *[Trickle Flow](#gloss:trickle-flow){.glossar-link}*`\index{Trickle Flow}`{=latex} aus [Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen), nur andersherum. $U$ ist die Jahreskapazität des Lieferanten, keine runde Zahl.
### B27 — Budgetlimit
@@ -27211,7 +27211,7 @@ for s in lieferanten:
$$\sum_i c_i x_i \le B$$
-Mit $x_i \in \{0,1\}$ ist das ein **Rucksackproblem**`\index{Rucksackproblem}`{=latex} — dasselbe Muster, das [Kapitel 6](#kap-milp) an `Rucksack.py` vorrechnet und [Kapitel 10](#kap-dekomposition) als Pricing-Teilproblem wiederverwendet.
+Mit $x_i \in \{0,1\}$ ist das ein **[Rucksackproblem](#gloss:rucksackproblem){.glossar-link}**`\index{Rucksackproblem}`{=latex} — dasselbe Muster, das [Kapitel 6](#kap-milp) an `Rucksack.py` vorrechnet und [Kapitel 10](#kap-dekomposition) als Pricing-Teilproblem wiederverwendet.
```python
modell.Add(sum(KOSTEN[i] * x[i] for i in massnahmen) <= BUDGET)
@@ -27221,7 +27221,7 @@ for topf, grenze in BUDGETS.items():
if TOPF[i] == topf) <= grenze)
```
-> 💡 **Der Schattenpreis`\index{Schattenpreis}`{=latex} des Budgets ist die Zahl, nach der die Geschäftsführung fragt:** „Was bringt der nächste Euro?“ Bei einem **LP** ist er direkt ablesbar. Bei **Ganzzahligkeit gibt es ihn nicht** — die Dualwerte der Relaxation`\index{Relaxation}`{=latex} sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte ([Abschnitt 5.9](#sec:lp-entartung)).
+> 💡 **Der [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} des Budgets ist die Zahl, nach der die Geschäftsführung fragt:** „Was bringt der nächste Euro?“ Bei einem **[LP](#gloss:lineare-programmierung-lp){.glossar-link}** ist er direkt ablesbar. Bei **Ganzzahligkeit gibt es ihn nicht** — die Dualwerte der [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex} sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte ([Abschnitt 5.9](#sec:lp-entartung)).
> ⚠️ **Die LP-Relaxation verspricht zu viel.** Sie darf die letzte Maßnahme anteilig kaufen und liefert deshalb eine Schranke, die spürbar über dem tatsächlich Erreichbaren liegen kann. Wer sie als Prognose berichtet, verspricht Geld, das nicht kommt.
@@ -27245,7 +27245,7 @@ Mit Mindestwartezeit $w$: $\text{start}_B \ge \text{ende}_A + w$.
modell.AddNoOverlap([intervall_1, intervall_2, ...])
```
-In MILP bräuchte man je Paar eine Disjunktion (B4) — bei $k$ Jobs sind das $\binom{k}{2}$ Konstruktionen. **Nehmen Sie hier CP-SAT`\index{CP-SAT}`{=latex}.**
+In [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} bräuchte man je Paar eine Disjunktion (B4) — bei $k$ Jobs sind das $\binom{k}{2}$ Konstruktionen. **Nehmen Sie hier [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}.**
### B15 — Kumulative Ressource
@@ -27308,7 +27308,7 @@ for t in perioden:
$$\sum_p x_{p,s} + u_s = 1,\qquad u_s\in\{0,1\}$$ Zielfunktion: $\dots + 10\,000\cdot u_s$
-Die Strafe muss **hoch genug** sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber **endlich**, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
+Die Strafe muss **hoch genug** sein, dass der [Solver](#gloss:solver){.glossar-link} sie nur im Notfall in Kauf nimmt, aber **endlich**, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.
### B19 — Hierarchische Ziele (lexikografisch)
@@ -27335,7 +27335,7 @@ loeser.Solve(modell)
$$\text{start}_1 \le \text{start}_2 \le \text{start}_3 \qquad\text{bzw.}\qquad \sum_j x_{1j} \ge \sum_j x_{2j}$$
-Ohne Symmetriebrechung durchsucht Branch-and-Bound`\index{Branch-and-Bound}`{=latex} $k!$ gleichwertige Lösungen. **Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.**
+Ohne Symmetriebrechung durchsucht [Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}`\index{Branch-and-Bound}`{=latex} $k!$ gleichwertige Lösungen. **Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.**
### B21 — Worst-Case-Abzug (robuste Formulierung)
@@ -27364,13 +27364,13 @@ $$\sum_{j:(i,j)\in E} x_{ij} - \sum_{k:(k,i)\in E} x_{ki} = b_i \qquad \forall i
$$\sum_j x_{ij} = 1\ \forall i,\qquad \sum_i x_{ij} = 1\ \forall j,\qquad x_{ij} \ge 0$$
-> 💡 **Ganzzahligkeit nicht fordern!** Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist `scipy.optimize.linear_sum_assignment` (Ungarischer Algorithmus`\index{Ungarischer Algorithmus}`{=latex}, $O(n^3)$) noch deutlich schneller.
+> 💡 **Ganzzahligkeit nicht fordern!** Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist `scipy.optimize.linear_sum_assignment` ([Ungarischer Algorithmus](#gloss:ungarischer-algorithmus){.glossar-link}`\index{Ungarischer Algorithmus}`{=latex}, $O(n^3)$) noch deutlich schneller.
### B24 — Subtour-Eliminierung
**MTZ (einfach, aber schwach):**`\index{MTZ-Formulierung}`{=latex} $$u_i - u_j + C x_{ij} \le C - d_j \qquad \forall i\ne j$$
-**Besser in der Praxis:**`\index{Subtour!Eliminierung}`{=latex} `AddCircuit` in CP-SAT oder die Routing-Bibliothek`\index{Routing-Bibliothek}`{=latex} von OR-Tools.
+**Besser in der Praxis:**`\index{Subtour!Eliminierung}`{=latex} `AddCircuit` in [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} oder die [Routing-Bibliothek](#gloss:routing-bibliothek){.glossar-link}`\index{Routing-Bibliothek}`{=latex} von [OR-Tools](#gloss:or-tools){.glossar-link}.
---
@@ -27399,17 +27399,17 @@ Wenn mehrere Muster passen, entscheiden Sie nach dieser Reihenfolge:
| Symptom | Abschnitt |
| --- | --- |
-| Solver meldet `INFEASIBLE` | [C1](#c1-infeasible) |
-| `INFEASIBLE`, aber keine einzelne Bedingung ist schuld | [C1](#c1-infeasible), Deletion Filter`\index{Deletion Filter}`{=latex} |
+| [Solver](#gloss:solver){.glossar-link} meldet `INFEASIBLE` | [C1](#c1-infeasible) |
+| `INFEASIBLE`, aber keine einzelne Bedingung ist schuld | [C1](#c1-infeasible), [Deletion Filter](#gloss:deletion-filter){.glossar-link}`\index{Deletion Filter}`{=latex} |
| Solver meldet `UNBOUNDED` | [C2](#c2-unbounded) |
| Solver läuft ewig / Timeout | [C3](#c3-zu-langsam) |
| Ergebnis ist offensichtlich unsinnig | [C4](#c4-unsinniges-ergebnis) |
| Ergebnis ändert sich bei kleinsten Datenänderungen stark | [C5](#c5-instabile-lösung) |
| Schattenpreise sehen falsch aus | [C6](#c6-falsche-dualwerte) |
| Zwei Solver liefern verschiedene Ergebnisse | [C7](#c7-widersprüchliche-solver) |
-| CVXPY`\index{CVXPY}`{=latex} wirft `DCPError` | [C8](#c8-dcperror) |
+| [CVXPY](#gloss:cvxpy){.glossar-link}`\index{CVXPY}`{=latex} wirft `DCPError` | [C8](#c8-dcperror) |
| `ImportError` bei ortools/highspy | [C9](#c9-importfehler) |
-| Backtest sieht zu gut aus | [C10](#c10-verdächtig-guter-backtest) |
+| [Backtest](#gloss:backtest){.glossar-link} sieht zu gut aus | [C10](#c10-verdächtig-guter-backtest) |
| Ergebnisse sind falsch beschriftet | [C11](#c11-vertauschte-spalten) |
---
@@ -27442,13 +27442,13 @@ strafen.append(10_000 * schlupf)
```
Nach dem Lösen zeigen die Schlupfvariablen mit Wert > 0 **präzise, welche Bedingung** wo und um wie viel verletzt werden musste. Das ist zugleich die produktionstaugliche Lösung (Muster B18).
-**Schritt 4 — Zeitfenster und Erreichbarkeit prüfen.** Bei Scheduling und Routing: Ist jeder Termin überhaupt physisch erreichbar?
+**Schritt 4 — Zeitfenster und Erreichbarkeit prüfen.** Bei [Scheduling](#gloss:scheduling){.glossar-link} und Routing: Ist jeder Termin überhaupt physisch erreichbar?
```python
for kunde, (frueh, spaet) in enumerate(zeitfenster):
assert distanz[depot][kunde] <= spaet, f"Kunde {kunde} nicht rechtzeitig erreichbar"
```
-**Schritt 5 — Rundungs- und Einheitenfehler.** Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen CP-SAT`\index{CP-SAT}`{=latex}-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: `sum(w) == 1` mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
+**Schritt 5 — Rundungs- und Einheitenfehler.** Sind Kapazitäten in Stunden, Verbräuche aber in Minuten? Rechnen [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex}-Modelle mit ganzen Zahlen, wo Sie Nachkommastellen brauchen? Ein klassischer Fall: `sum(w) == 1` mit Gleitkommazahlen — nutzen Sie eine Toleranz oder rechnen Sie in Ganzzahlen (Promille).
### Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen
@@ -27458,7 +27458,7 @@ for kunde, (frueh, spaet) in enumerate(zeitfenster):
> Nimm eine Bedingung versuchsweise heraus. Bleibt der Rest unlösbar, wurde sie nicht gebraucht: weg damit, endgültig. Wird der Rest lösbar, war sie beteiligt: sie bleibt.
-Nach genau einem Durchlauf über alle $n$ Bedingungen ist die übrig gebliebene Menge`\index{Konvexität!Menge}`{=latex} **unreduzierbar** — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein *Irreducible Infeasible Subset*`\index{IIS (Irreducible Infeasible Subset)}`{=latex}, kurz IIS. Kommerzielle Solver bieten das als fertige Funktion`\index{Konvexität!Funktion}`{=latex} an (`computeIIS` bei Gurobi`\index{Gurobi}`{=latex}, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet $n$ Aufrufe des Solvers statt $2^n$ durchprobierter Teilmengen.
+Nach genau einem Durchlauf über alle $n$ Bedingungen ist die übrig gebliebene Menge`\index{Konvexität!Menge}`{=latex} **unreduzierbar** — entfernt man aus ihr irgendeine Bedingung, ist der Rest lösbar. Das ist ein *Irreducible Infeasible Subset*`\index{IIS (Irreducible Infeasible Subset)}`{=latex}, kurz [IIS](#gloss:iis-irreducible-infeasible-subset){.glossar-link}. Kommerzielle Solver bieten das als fertige Funktion`\index{Konvexität!Funktion}`{=latex} an (`computeIIS` bei [Gurobi](#gloss:gurobi){.glossar-link}`\index{Gurobi}`{=latex}, der Conflict Refiner bei CPLEX); im Open-Source-Werkzeugkasten dieses Buchs gibt es sie nicht — sie ist aber in zwanzig Zeilen selbst geschrieben, und sie kostet $n$ Aufrufe des Solvers statt $2^n$ durchprobierter Teilmengen.
```python
#!/usr/bin/env python3
@@ -27799,7 +27799,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
**Vier Dinge sind daran wichtig.**
-1. **Die Zulässigkeitsprüfung braucht keine Zielfunktion`\index{Zielfunktion}`{=latex}.** `linprog` bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
+1. **Die Zulässigkeitsprüfung braucht keine [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}.** `linprog` bekommt einen Nullvektor als Ziel. Unlösbarkeit hängt nie an der Zielfunktion — wer beim Diagnostizieren die echte Zielfunktion mitschleppt, bezahlt Rechenzeit für nichts.
2. **Gefiltert werden nur die aufgeführten Bedingungen, nicht die Variablenschranken.** Die Nichtnegativität steht in `bounds` und bleibt in jedem Teilmodell stehen. Sitzt Ihr Widerspruch in den Schranken (`x >= 5` als Bound statt als Zeile), findet ihn der Filter nicht — schreiben Sie solche Grenzen dann als benannte Bedingung.
3. **Sprechende Namen sind kein Luxus.** Der Befund ist genau so brauchbar wie die Namen, die darin vorkommen. `constraint_47, constraint_112, constraint_9` ist kein Befund.
4. **Der Filter liefert *einen* kleinsten Konflikt, nicht *den* kleinsten.** Dieses Modell mit vierzehn Bedingungen enthält neun verschiedene minimale Konflikte; welchen man zu sehen bekommt, entscheidet allein die Prüfreihenfolge. Das ist kein Mangel des Verfahrens, sondern eine Eigenschaft des Problems.
@@ -27808,7 +27808,7 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.
> **⚠️ Was der Filter kostet** Ein Konflikt kostet $n$ Solverläufe. Bei 14 Bedingungen sind das 14 — bei 50 000 Restriktionen mit je zehn Sekunden Lösungszeit wären es knapp sechs Tage. Für große Modelle filtert man deshalb nicht einzeln, sondern **gruppenweise**: erst über Bedingungs*blöcke* (alle Kapazitäten, alle Verträge, alle Zeitfenster) laufen, dann den Filter nur noch innerhalb des einen Blocks anwenden, der übrig bleibt. Das ist derselbe Algorithmus auf einer gröberen Ebene und senkt die Zahl der Läufe um Größenordnungen.
-**Das Verhältnis zur Relaxation`\index{Relaxation}`{=latex}.** Der Deletion Filter beantwortet die Frage *„was widerspricht sich?“*; die Schlupfvariablen aus Schritt 3 beantworten die Frage *„was tun wir jetzt?“*. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (`Infeasibility_Diagnose.py`, [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)). Ein System, das im Ernstfall den Konflikt *benennt* und trotzdem einen Notfallplan *liefert*, hat beides.
+**Das Verhältnis zur [Relaxation](#gloss:relaxation){.glossar-link}`\index{Relaxation}`{=latex}.** Der Deletion Filter beantwortet die Frage *„was widerspricht sich?“*; die Schlupfvariablen aus Schritt 3 beantworten die Frage *„was tun wir jetzt?“*. Beide gehören in ein Produktionssystem, aber an verschiedene Stellen: der Filter in die Entwicklung und in die Fehlersuche, die Relaxation in den Betrieb (`Infeasibility_Diagnose.py`, [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)). Ein System, das im Ernstfall den Konflikt *benennt* und trotzdem einen Notfallplan *liefert*, hat beides.
> **🎯 Die eigentliche Lehre** `INFEASIBLE` in Produktion ist ein **Entwurfsfehler**, kein Betriebsfehler. Ein System, das im Ernstfall nur „geht nicht“ sagt, ist wertlos. Bauen Sie Schlupfvariablen von vornherein ein ([Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)).
@@ -27848,11 +27848,11 @@ print(f"Aufbau {t1-t0:.2f}s | Loesen {t2-t1:.2f}s")
```
Bei CVXPY und bei Schleifen über Szenarien ist oft der **Aufbau** der Engpass, nicht der Solver (siehe [Abschnitt 20.6](#sec:cvar-implementierung-cvar-portfolio-mit-reibung)).
-**2. Big-M zu groß?** Der häufigste Grund für explodierende MILP-Laufzeiten. Setzen Sie $M$ auf die kleinste gültige Schranke ([Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)).
+**2. Big-M zu groß?** Der häufigste Grund für explodierende [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link}-Laufzeiten. Setzen Sie $M$ auf die kleinste gültige Schranke ([Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)).
**3. Symmetrie im Modell?** Identische Maschinen, austauschbare Mitarbeitende, gleichwertige Fahrzeuge — der Solver durchsucht alle Vertauschungen. Abhilfe: Ordnungsbedingungen (Muster B20).
-**4. Falsche Solverfamilie?** Zuordnungsprobleme mit MILP, Routing mit CP-SAT von Hand nachgebaut, konvexe Probleme mit `scipy.optimize.minimize` — jeweils Größenordnungen langsamer als das passende Werkzeug ([Abschnitt 3.6](#sec:oekosystem-wann-lohnt-sich-welche-ebene)).
+**4. Falsche Solverfamilie?** Zuordnungsprobleme mit MILP, Routing mit [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} von Hand nachgebaut, konvexe Probleme mit `scipy.optimize.minimize` — jeweils Größenordnungen langsamer als das passende Werkzeug ([Abschnitt 3.6](#sec:oekosystem-wann-lohnt-sich-welche-ebene)).
**5. Wird vektorisiert?**
```python
@@ -27904,9 +27904,9 @@ Diese Zeilen kosten Sekunden und fangen die Fehlerklasse ab, die der Solver **ni
| Ursache | Prüfung | Abhilfe |
| --- | --- | --- |
-| Schlecht konditionierte Kovarianzmatrix`\index{Kovarianzmatrix}`{=latex} | `np.linalg.cond(Sigma)` | Ledoit-Wolf-Shrinkage`\index{Ledoit-Wolf-Shrinkage}`{=latex} ([Kapitel 18](#kap-finanzdaten)) |
+| Schlecht konditionierte [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}`\index{Kovarianzmatrix}`{=latex} | `np.linalg.cond(Sigma)` | [Ledoit-Wolf-Shrinkage](#gloss:ledoit-wolf-shrinkage){.glossar-link}`\index{Ledoit-Wolf-Shrinkage}`{=latex} ([Kapitel 18](#kap-finanzdaten)) |
| Zu wenige Beobachtungen | $T/N$ berechnen | Historie verlängern oder Universum verkleinern |
-| Alternativoptima`\index{Alternativoptima}`{=latex} | Zielfunktion bei mehreren Lösungen gleich? | Regularisierung: kleinen $L_2$-Term addieren |
+| [Alternativoptima](#gloss:alternativoptima){.glossar-link}`\index{Alternativoptima}`{=latex} | Zielfunktion bei mehreren Lösungen gleich? | Regularisierung: kleinen $L_2$-Term addieren |
| Fehlende Turnover-Dämpfung | Turnover messen | $L_1$-Strafe ([Abschnitt 20.5](#sec:cvar-transaktionskosten-ueber-die-l-1-norm)) |
```python
@@ -27917,7 +27917,7 @@ print(f"Kondition {eig.max()/eig.min():.0f} | kleinster EW {eig.min():.2e}")
### Derselbe Fehler außerhalb der Finanzwelt
-Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge ($10^7$) und Tonnen-Angaben ($10^{-3}$) in derselben Matrix erzeugen eine Konditionszahl`\index{Konditionszahl}`{=latex}, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
+Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge ($10^7$) und Tonnen-Angaben ($10^{-3}$) in derselben Matrix erzeugen eine [Konditionszahl](#gloss:konditionszahl){.glossar-link}`\index{Konditionszahl}`{=latex}, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.
```python
print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")
@@ -27925,7 +27925,7 @@ print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")
print(f"kleinster / groesster Betrag: {np.abs(A[A != 0]).min():.1e} "
f"/ {np.abs(A).max():.1e}")
```
-Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. `Skalierung_Kondition.py` zeigt beides samt Ruiz-Equilibrierung`\index{Ruiz-Equilibrierung}`{=latex} ([Abschnitt 2.7](#sec:fundament-kondition)).
+Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. `Skalierung_Kondition.py` zeigt beides samt [Ruiz-Equilibrierung](#gloss:ruiz-equilibrierung){.glossar-link}`\index{Ruiz-Equilibrierung}`{=latex} ([Abschnitt 2.7](#sec:fundament-kondition)).
---
@@ -27941,12 +27941,12 @@ schattenpreise = -res.ineqlin.marginals # bei linprog nach Negation
```
Genau das ist die Vorzeichenfalle`\index{Schattenpreis!Vorzeichenfalle}`{=latex} aus [Abschnitt 5.7](#sec:lp-die-vorzeichenfalle-bei-schattenpreisen).
-**2. Komplementärer Schlupf`\index{Komplementärer Schlupf}`{=latex}.**
+**2. [Komplementärer Schlupf](#gloss:komplementaerer-schlupf){.glossar-link}`\index{Komplementärer Schlupf}`{=latex}.**
```python
for s, y in zip(res.slack, schattenpreise):
assert abs(s * y) < 1e-6, "Komplementaerer Schlupf verletzt!"
```
-Eine Ressource mit Reserve **muss** Schattenpreis`\index{Schattenpreis}`{=latex} 0 haben.
+Eine Ressource mit Reserve **muss** [Schattenpreis](#gloss:schattenpreis){.glossar-link}`\index{Schattenpreis}`{=latex} 0 haben.
**3. Numerische Gegenprobe — der zuverlässigste Test.**
```python
@@ -27956,7 +27956,7 @@ assert abs(zuwachs - schattenpreise[i]) < 1e-6
```
Dieser Test ist **unabhängig von jeder Vorzeichenkonvention**. Nutzen Sie ihn.
-**4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung`\index{Entartung}`{=latex}.** Die drei Prüfungen oben setzen voraus, dass es *einen* richtigen Schattenpreis gibt. Bei einem entarteten Optimum`\index{Entartung!und Dualwerte}`{=latex} — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern **mehrdeutig**: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
+**4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: [Entartung](#gloss:entartung){.glossar-link}`\index{Entartung}`{=latex}.** Die drei Prüfungen oben setzen voraus, dass es *einen* richtigen Schattenpreis gibt. Bei einem entarteten Optimum`\index{Entartung!und Dualwerte}`{=latex} — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern **mehrdeutig**: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.
```python
aktiv = int(np.sum(res.slack < 1e-9)) # wie viele Bedingungen sind straff?
@@ -27986,15 +27986,15 @@ Die belastbare Antwort ist dann keine Zahl, sondern eine **Spanne** über alle o
cvxpy.error.DCPError: Problem does not follow DCP rules.
```
-**Bedeutung.**`\index{DCPError}`{=latex} CVXPY kann die Konvexität`\index{Konvexität}`{=latex} Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. **Das ist ein Feature.**
+**Bedeutung.**`\index{DCPError}`{=latex} CVXPY kann die [Konvexität](#gloss:konvexitaet){.glossar-link}`\index{Konvexität}`{=latex} Ihres Ausdrucks nicht beweisen — es lehnt ab, statt ein Ergebnis ohne Garantie zu liefern. **Das ist ein Feature.**
| Problematischer Ausdruck | Warum | Lösung |
| ------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------- |
-| `x * y` (beide Variablen) | bilinear | umformulieren oder MILP/NLP |
+| `x * y` (beide Variablen) | bilinear | umformulieren oder MILP/[NLP](#gloss:nichtlineare-programmierung-nlp){.glossar-link} |
| `a / x` | nicht konvex | `cp.inv_pos(x)` bei $x>0$ |
-| `cp.sqrt(cp.quad_form(w, S))` | `cp.sqrt` ist **konkav** und verlangt ein konkaves Argument — `quad_form` ist konvex. Der Ausdruck ist nie DCP, egal wie sauber $S$ ist | Cholesky $S = LL^\top$, dann `cp.norm2(L.T @ w)`: die 2-Norm eines **affinen** Ausdrucks (siehe `CVaR_Portfolio.py`) |
+| `cp.sqrt(cp.quad_form(w, S))` | `cp.sqrt` ist **konkav** und verlangt ein konkaves Argument — `quad_form` ist konvex. Der Ausdruck ist nie [DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link}, egal wie sauber $S$ ist | Cholesky $S = LL^\top$, dann `cp.norm2(L.T @ w)`: die 2-Norm eines **affinen** Ausdrucks (siehe `CVaR_Portfolio.py`) |
| `cp.quad_form(w, S)` bei numerisch unsauberem $S$ | Eigenwerte knapp unter null durch Rundung — CVXPY erkennt $S$ nicht als PSD | `cp.psd_wrap(S)`, **wenn** $S$ nachweislich PSD ist (z. B. eine Kovarianzmatrix). Behebt nur diese numerische Beanstandung, keine Regelverletzung im Aufbau |
-| Quotient (z. B. Sharpe Ratio`\index{Sharpe-Ratio}`{=latex}) | nicht konvex | Korn-Transformation`\index{Korn-Transformation}`{=latex} ([Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)) |
+| Quotient (z. B. [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}`\index{Sharpe-Ratio}`{=latex}) | nicht konvex | [Korn-Transformation](#gloss:korn-transformation){.glossar-link}`\index{Korn-Transformation}`{=latex} ([Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)) |
| `cp.log(x)` maximieren | konkav — das ist **erlaubt** | in `cp.Maximize` verwenden |
```python
@@ -28012,7 +28012,7 @@ print(problem.objective.is_dcp())
ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryEv
```
-**Ursache.**`\index{Importfehler (ortools und highspy)}`{=latex} `ortools` und `highspy` bringen beide eine eigene HiGHS`\index{HiGHS}`{=latex}-Kopie mit; sie lassen sich auf vielen Systemen **nicht im selben Prozess** importieren (siehe [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze)). Der Konflikt entsteht auch **indirekt**: `cvxpy` importiert ein installiertes `highspy` bei der Solver-Erkennung selbst mit — ein Skript, das erst `cvxpy` und dann `ortools` importiert, crasht daher mit derselben Meldung.
+**Ursache.**`\index{Importfehler (ortools und highspy)}`{=latex} `ortools` und `highspy` bringen beide eine eigene [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex}-Kopie mit; sie lassen sich auf vielen Systemen **nicht im selben Prozess** importieren (siehe [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze)). Der Konflikt entsteht auch **indirekt**: `cvxpy` importiert ein installiertes `highspy` bei der Solver-Erkennung selbst mit — ein Skript, das erst `cvxpy` und dann `ortools` importiert, crasht daher mit derselben Meldung.
**Abhilfen (in dieser Reihenfolge):**
1. Nur eines von beiden im selben Skript verwenden.
@@ -28083,7 +28083,7 @@ tech_indices = [0, 1, 2, 3] # SCHLECHT
$$\max\; 10x_1 + 15x_2 + 25x_3 \quad\text{u.d.N.}\quad x_1 + x_2 + 2x_3 \le 40,\;\; 2x_1 + 3x_2 + x_3 \le 50,\;\; x \ge 0$$
-Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist CP-SAT`\index{CP-SAT}`{=latex} — ein rein stetiges LP ist dort das falsche Werkzeug; die Seite zeigt stattdessen die CP-SAT-eigenen Bausteine.)
+Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530 heraus, liegt es an der Übertragung, nicht am Modell. (Die Ausnahme ist [CP-SAT](#gloss:cp-sat){.glossar-link}`\index{CP-SAT}`{=latex} — ein rein stetiges [LP](#gloss:lineare-programmierung-lp){.glossar-link} ist dort das falsche Werkzeug; die Seite zeigt stattdessen die [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link}-eigenen Bausteine.)
> **Was dieser Anhang *nicht* ist.** Kein Vergleich und keine Empfehlung. Welche Bibliothek für welche Aufgabe taugt, steht in [Abschnitt 3.6](#sec:oekosystem-wann-lohnt-sich-welche-ebene); denselben Fall in vier Bibliotheken *nebeneinander* zeigt `Ein_System_Vier_Ansaetze.py`, die beiden Modellierungssprachen `Modellierungsschichten.py`. Hier geht es allein ums Nachschlagen.
@@ -28091,7 +28091,7 @@ Damit ist jeder Schnipsel selbstprüfend: Kommt bei Ihnen etwas anderes als 530
## D1 — SciPy: `linprog` und `milp`
-**Wofür.** Die Einstiegsschicht: keine zusätzliche Installation, HiGHS`\index{HiGHS}`{=latex} als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform`\index{Matrixform}`{=latex}. **Wofür nicht:** alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion`\index{Zielfunktion}`{=latex}.
+**Wofür.** Die Einstiegsschicht: keine zusätzliche Installation, [HiGHS](#gloss:highs){.glossar-link}`\index{HiGHS}`{=latex} als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform`\index{Matrixform}`{=latex}. **Wofür nicht:** alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare [Zielfunktion](#gloss:zielfunktion){.glossar-link}`\index{Zielfunktion}`{=latex}.
### Lineares Programm
@@ -28157,7 +28157,7 @@ else:
## D2 — HiGHS über `highspy`
-**Wofür.** Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. **Wofür nicht:** schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
+**Wofür.** Derselbe [Solver](#gloss:solver){.glossar-link} wie unter [SciPy](#gloss:scipy){.glossar-link}, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. **Wofür nicht:** schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.
```python
import numpy as np
@@ -28200,14 +28200,14 @@ else:
### Die drei häufigsten Stolpersteine
1. **Nicht zusammen mit `ortools` importieren.** Beide bringen eine eigene HiGHS-Kopie mit; im selben Prozess endet das in `undefined symbol` ([Anhang C](#anhang-fehlerdiagnose), C9). Auch `cvxpy` zieht `highspy` bei der Solver-Erkennung mit hinein.
-2. **Das CSR-Format`\index{CSR-Format}`{=latex} stimmt oder es stimmt still nicht.** `starts` hat so viele Einträge wie Zeilen, `indices` und `values` so viele wie Nichtnullen. Ein falscher `starts`-Eintrag erzeugt ein *anderes*, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
+2. **Das [CSR-Format](#gloss:csr-format){.glossar-link}`\index{CSR-Format}`{=latex} stimmt oder es stimmt still nicht.** `starts` hat so viele Einträge wie Zeilen, `indices` und `values` so viele wie Nichtnullen. Ein falscher `starts`-Eintrag erzeugt ein *anderes*, aber lösbares Modell — es fällt nur durch ein falsches Ergebnis auf.
3. **`output_flag` abschalten**, sonst überschwemmt das Solverprotokoll jede Ausgabe.
---
## D3 — OR-Tools`\index{OR-Tools}`{=latex}: `pywraplp`
-**Wofür.** Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (`GLOP`, `SCIP`, `CBC`, `SAT`). **Wofür nicht:** Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
+**Wofür.** Bequeme algebraische Schreibweise für LP und [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} mit umschaltbarem Backend (`GLOP`, `SCIP`, `CBC`, `SAT`). **Wofür nicht:** [Scheduling](#gloss:scheduling){.glossar-link} und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.
```python
from ortools.linear_solver import pywraplp
@@ -28299,7 +28299,7 @@ print(f"Laufzeit {loeser.WallTime():.3f}s, {loeser.NumBranches()} Verzweigungen"
## D5 — CVXPY`\index{CVXPY}`{=latex}
-**Wofür.** Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. **Wofür nicht:** große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
+**Wofür.** Konvexe Probleme: quadratische Ziele, Normen, [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link}, alles mit Regularisierungstermen. **Wofür nicht:** große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.
```python
import numpy as np
@@ -28332,7 +28332,7 @@ else:
### Die drei häufigsten Stolpersteine
-1. **DCP-Regeln vor dem Lösen prüfen.** `problem.is_dcp()` und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen `DCPError` ohne Ort zu werfen ([Anhang C](#anhang-fehlerdiagnose), C8).
+1. **[DCP](#gloss:dcp-disciplined-convex-programming){.glossar-link}-Regeln vor dem Lösen prüfen.** `problem.is_dcp()` und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen `DCPError` ohne Ort zu werfen ([Anhang C](#anhang-fehlerdiagnose), C8).
2. **`OPTIMAL_INACCURATE` ist kein Erfolg.** Der Solver hat aufgegeben und meldet das leise. Diesen Fall immer eigens behandeln.
3. **Der Aufbau kann teurer sein als das Lösen.** Schleifen über Szenarien durch Vektorausdrücke ersetzen; `Parameter` statt Neuaufbau, wenn sich nur Zahlen ändern.
@@ -28340,7 +28340,7 @@ else:
## D6 — Modellierungssprachen: Pyomo`\index{Pyomo}`{=latex} und Linopy`\index{Linopy}`{=latex}
-**Wofür.** Beide trennen *Modell* von *Solver*: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi`\index{Gurobi}`{=latex}. Pyomo denkt in **Mengen und Indizes** wie eine mathematische Formulierung; Linopy denkt in **beschrifteten Arrays** und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. **Wofür nicht:** ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
+**Wofür.** Beide trennen *Modell* von *Solver*: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, [Gurobi](#gloss:gurobi){.glossar-link}`\index{Gurobi}`{=latex}. [Pyomo](#gloss:pyomo){.glossar-link} denkt in **Mengen und Indizes** wie eine mathematische Formulierung; [Linopy](#gloss:linopy){.glossar-link} denkt in **beschrifteten Arrays** und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. **Wofür nicht:** ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.
### Pyomo
@@ -28439,521 +28439,1009 @@ Nachschlagewerk zu allen Fachbegriffen, Abkürzungen, Algorithmen und Bibliothek
## A
+::: {#gloss:absolutbetrag}
**Absolutbetrag**`\index{Absolutbetrag (Modellierungsmuster)}`{=latex} — Modellierungsmuster für $|x-z|$: Der Betrag selbst ist nicht linear, lässt sich aber durch eine Hilfsvariable $d$ mit den beiden Bedingungen $x-z \le d$ und $z-x \le d$ ersetzen. Das funktioniert nur, wenn $d$ minimiert wird — sonst bleibt die Schranke unausgeschöpft. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:adjusted-close}
**Adjusted Close**`\index{Adjusted Close}`{=latex} — Um Dividenden und Splits bereinigter Schlusskurs. Nur mit ihm sind Renditen über längere Zeiträume vergleichbar; der unbereinigte Schlusskurs erzeugt an jedem Ausschüttungstag einen Scheinverlust. → [Abschnitt 18.3](#sec:finanzdaten-diskrete-und-logarithmische-renditen)
+:::
+::: {#gloss:aktivierungsschalter}
**Aktivierungsschalter**`\index{Aktivierungsschalter (Modellierungsmuster)}`{=latex} — Modellierungsmuster, das eine kontinuierliche Größe an eine Binärvariable koppelt: $x \le M\,y$ erlaubt $x > 0$ nur, wenn $y = 1$ ist. Grundbaustein für Fixkosten, Standortentscheidungen und Mindestmengen. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:almgren-chriss-modell}
**Almgren-Chriss-Modell**`\index{Almgren-Chriss-Modell}`{=latex} — Standardmodell der optimalen Orderausführung. Es löst den Zielkonflikt zwischen Marktauswirkung bei schnellem Handeln und Volatilitätsrisiko bei langsamem Handeln und liefert einen Ausführungsplan über mehrere Perioden. → [Abschnitt 13.4](#sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung)
+:::
+::: {#gloss:alternativoptima}
**Alternativoptima**`\index{Alternativoptima}`{=latex} — Mehrere Lösungen mit identischem Zielfunktionswert. Sie treten auf, wenn die Zielfunktion parallel zu einer Kante des Polyeders verläuft oder die Matrix des quadratischen Terms nur semidefinit ist. Der Solver meldet dann eine beliebige davon — zwei Läufe können unterschiedlich aussehen und trotzdem beide optimal sein. → [Abschnitt 11.3](#sec:qp-nlp-das-quadratische-programm-qp)
+:::
+::: {#gloss:approximate-dynamic-programming}
**Approximate Dynamic Programming**`\index{Approximate Dynamic Programming}`{=latex} — Sammelbegriff für Verfahren, die die Wertfunktion der dynamischen Programmierung nicht exakt tabellieren, sondern annähern — etwa durch Regression oder ein neuronales Netz. Antwort auf den Fluch der Dimensionalität, wenn der Zustandsraum zu groß zum Durchzählen wird. → [Abschnitt 22.9](#sec:praxisfallen-weiterfuehrende-literatur-und-roadmap)
+:::
## B
+::: {#gloss:backtest}
+**Backtest**`\index{Backtest}`{=latex} — Rückblickender Test einer Strategie auf historischen Daten. Ein Backtest ist nur so glaubwürdig wie seine Daten, seine Kostenannahme und seine Trennung von Trainings- und Testzeitraum — [Kapitel 21](#kap-handelsmaschine) zeigt die fünf Selbsttäuschungen, die ihn wertlos machen. → [Kapitel 21](#kap-handelsmaschine)
+:::
+
+::: {#gloss:bedingte-kopplung}
**Bedingte Kopplung**`\index{Bedingte Kopplung (Konjunktion)}`{=latex} — Modellierungsmuster für „wenn A **und** B, dann C“. Umgesetzt über $y_C \ge y_A + y_B - 1$: Nur wenn beide Voraussetzungen gesetzt sind, wird die Folge erzwungen. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:bellman-gleichung}
**Bellman-Gleichung**`\index{Bellman-Gleichung}`{=latex} — Rekursionsgleichung der dynamischen Programmierung: Der Wert eines Zustands ist die Summe aus den unmittelbaren Kosten der besten Aktion und dem Wert des Folgezustands. Sie zerlegt eine mehrstufige Entscheidung in eine Kette einstufiger. → [Abschnitt 13.3](#sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip)
+:::
+::: {#gloss:benders-zerlegung}
**Benders-Zerlegung**`\index{Benders-Zerlegung}`{=latex} — Dekompositionsverfahren für Probleme mit wenigen „schweren“ und vielen „leichten“ Variablen. Ein Masterproblem entscheidet über die schweren, ein Subproblem über den Rest und schickt Schnitte (Benders-Cuts) zurück, bis beide übereinstimmen. → [Abschnitt 22.9](#sec:praxisfallen-weiterfuehrende-literatur-und-roadmap)
+:::
+::: {#gloss:big-m-methode}
**Big-M-Methode**`\index{Big-M-Methode}`{=latex} — Modellierungstrick, der logische Bedingungen über eine hinreichend große Konstante $M$ an eine Binärvariable koppelt. $M$ sollte **so klein wie möglich** gewählt werden: Große Werte weichen die Relaxation auf, verschlechtern die Laufzeit und können zu Trickle Flow führen. → [Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)
+:::
+::: {#gloss:binaervariable}
**Binärvariable**`\index{Binärvariable}`{=latex} — Entscheidungsvariable mit Wertebereich $\{0,1\}$. Sie schaltet Fixkosten, Kapazitäten oder logische Alternativen an und aus und macht aus einem linearen Programm ein gemischt-ganzzahliges. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:bipartites-matching}
**Bipartites Matching**`\index{Bipartites Matching}`{=latex} — Zuordnung zwischen zwei disjunkten Mengen — etwa Personen und Aufgaben — mit maximalem Nutzen oder minimalen Kosten. Klassisch gelöst durch den Ungarischen Algorithmus; die Modellmatrix ist total unimodular, weshalb schon das LP ganzzahlige Ecken hat. → [Abschnitt 8.4](#sec:graphen-bipartites-matching-das-zuordnungsproblem)
+:::
+::: {#gloss:black-box-effekt}
**Black-Box-Effekt**`\index{Black-Box-Effekt}`{=latex} — Ablehnung eines Optimierungssystems, weil seine Entscheidungen nicht nachvollziehbar sind. Gegenmittel sind ein Constraint-Trace für Einzelentscheidungen und eine Kostenzerlegung für Bewertungen. Akzeptanz ist keine Nebensache, sondern Betriebsvoraussetzung. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:branch-and-bound}
**Branch-and-Bound**`\index{Branch-and-Bound}`{=latex} — Exaktes Verfahren für ganzzahlige Probleme: Der Suchraum wird rekursiv zerlegt (Branching), und ein Zweig wird verworfen, sobald seine Relaxation schlechter ist als die beste bekannte Lösung (Bounding). Ohne das Abschneiden wäre es bloßes Durchprobieren. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
+::: {#gloss:branch-and-cut}
**Branch-and-Cut**`\index{Branch-and-Cut}`{=latex} — Kombination aus Branch-and-Bound und Schnittebenenverfahren: In den Knoten des Suchbaums werden zusätzlich gültige Ungleichungen erzeugt, die gebrochene Lösungen abschneiden. Der Standardalgorithmus heutiger MILP-Solver. → [Abschnitt 3.3](#sec:oekosystem-die-zwei-schichten-architektur)
+:::
+::: {#gloss:brute-force}
**Brute Force**`\index{Brute Force}`{=latex} — Vollständiges Durchprobieren aller Möglichkeiten. Für kleine Instanzen ein nützlicher Kontrollwert, in der Praxis meist chancenlos: Schon 20 binäre Entscheidungen ergeben über eine Million Kombinationen. → [Abschnitt 1.3](#sec:einfuehrung-was-ist-operations-research-wirklich)
+:::
+::: {#gloss:budgeted-uncertainty}
**Budgeted Uncertainty**`\index{Budgeted Uncertainty}`{=latex} — Robustheitsansatz nach Bertsimas und Sim: Höchstens $\Gamma$ von $n$ Parametern nehmen gleichzeitig ihren ungünstigsten Wert an. Der Parameter $\Gamma$ regelt stufenlos, wie vorsichtig die Lösung wird — bei $\Gamma = 0$ nominal, bei $\Gamma = n$ maximal pessimistisch. → [Abschnitt 12.6](#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case)
+:::
+::: {#gloss:budgetlimit}
**Budgetlimit**`\index{Budgetlimit}`{=latex} — Nebenbedingung, die die Summe gewichteter Entscheidungen nach oben begrenzt. Der zugehörige Schattenpreis beantwortet unmittelbar, was eine zusätzliche Budgeteinheit einbrächte. → [Anhang B](#anhang-modellierungsmuster)
+:::
## C
+::: {#gloss:calmar-ratio}
**Calmar Ratio**`\index{Calmar Ratio}`{=latex} — Jahresrendite geteilt durch den Betrag des maximalen Drawdowns. Ergänzt die Sharpe Ratio um die Verlustperspektive: Sie misst Ertrag je Einheit des schlimmsten erlebten Rückschlags statt je Einheit Schwankung. → [Abschnitt 21.5](#sec:handelsmaschine-die-engine)
+:::
+::: {#gloss:cdcl}
**CDCL**`\index{CDCL}`{=latex} — *Conflict-Driven Clause Learning* — das Suchprinzip moderner SAT-Solver. Aus jedem Widerspruch wird eine Sperrklausel abgeleitet, die verhindert, dass dieselbe Sackgasse erneut betreten wird; anschließend springt die Suche gezielt zurück statt Schritt für Schritt. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:chance-constraint}
**Chance Constraint**`\index{Chance Constraint}`{=latex} — Nebenbedingung, die nicht immer, sondern nur mit vorgegebener Wahrscheinlichkeit gelten muss — etwa „die Versorgung reicht in 95 % der Fälle“. Bei normalverteilter Unsicherheit wird daraus eine konische Bedingung, sonst hilft eine szenariobasierte Big-M-Formulierung. → [Abschnitt 12.7](#sec:unsicherheit-chance-constraints)
+:::
+::: {#gloss:column-generation}
**Column Generation**`\index{Column Generation}`{=latex} — Verfahren für Modelle mit sehr vielen Variablen: Gerechnet wird auf einer kleinen Teilmenge, und ein Preisproblem sucht wiederholt die eine Spalte, die sich noch lohnt. Erst wenn keine mehr existiert, ist das Optimum bewiesen. → [Abschnitt 9.8](#sec:metaheuristiken-abgrenzung)
+:::
+::: {#gloss:conditional-value-at-risk-cvar}
**Conditional Value at Risk (CVaR)**`\index{Conditional Value at Risk (CVaR)}`{=latex} — Erwarteter Verlust in den schlechtesten Szenarien jenseits des Value at Risk. Anders als der VaR ist der CVaR kohärent und subadditiv und lässt sich nach Rockafellar und Uryasev exakt als lineares Programm formulieren. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:conflict-learning}
**Conflict Learning**`\index{CDCL!Conflict Learning}`{=latex} — Technik moderner SAT- und CP-Solver, aus jedem erkannten Widerspruch eine Sperrklausel abzuleiten. Der Solver lernt so aus Fehlschlägen, statt gleichartige Sackgassen immer wieder zu durchsuchen. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:constraint-attribution}
**Constraint Attribution**`\index{Constraint Attribution}`{=latex} — Diagnoseverfahren für den häufigsten Praxisfall: Das Modell rechnet, der Plan ist zulässig — und trotzdem enttäuschend. Jede Bedingungsgruppe wird einzeln gelockert und gemessen, wie sich der Zielwert ändert. Dasselbe Vorgehen wie der Deletion Filter, nur mit Kosten statt Zulässigkeit als Kriterium. → [Abschnitt 22.4](#sec:praxisfallen-attribution)
+:::
+::: {#gloss:constraint-programming-cp}
**Constraint Programming (CP)**`\index{Constraint Programming (CP)}`{=latex} — Paradigma, das nicht über Zielfunktionsgradienten, sondern über logische Verträglichkeit sucht: Constraints schränken Wertebereiche ein, bis eine zulässige Belegung gefunden ist. Stark bei Reihenfolgen, Schichtplänen und Ressourcenkonflikten. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:constraint-propagation}
**Constraint Propagation**`\index{Constraint Propagation}`{=latex} — Kernmechanismus von CP-Solvern: Aus einer Zuweisung werden unmögliche Werte anderer Variablen sofort entfernt. Der Suchbaum schrumpft dadurch, bevor er überhaupt durchsucht wird. Genau hier liegt der Vorteil globaler Constraints — ihre Propagatoren schließen weit mehr Werte aus als die gleichwertige Menge einzelner Ungleichungen. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:constraint-trace}
**Constraint-Trace**`\index{Constraint-Trace}`{=latex} — Nachvollziehbare Begründung einer einzelnen Solver-Entscheidung — „Warum bekommt Person A die Stunde und nicht Person B?“ — anhand der bindenden Bedingungen und Strafpunkte. Das Gegenmittel zum Black-Box-Effekt. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:cp-sat}
**CP-SAT**`\index{CP-SAT}`{=latex} — Constraint-Programming-Solver von OR-Tools. Er übersetzt ein Modell in boolesche Erfüllbarkeit und löst es mit Propagation, Conflict Learning und paralleler Suche. Für Reihenfolge- und Zuordnungsprobleme meist schneller als ein MILP-Solver. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:cp-sat-solver}
**CP-SAT-Solver**`\index{CP-SAT-Solver}`{=latex} — Die Solver-Komponente von OR-Tools, angesprochen über `cp_model.CpSolver()`. Sie liefert neben der Lösung Statuscode, Laufzeit und die beste bekannte Schranke und lässt sich über `num_workers` auf mehrere Kerne verteilen. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:csr-format}
**CSR-Format**`\index{CSR-Format}`{=latex} — *Compressed Sparse Row* — Speicherformat für dünn besetzte Matrizen, das nur die von null verschiedenen Einträge samt ihrer Positionen ablegt. Für Optimierungsmodelle mit zehntausenden Nebenbedingungen der Unterschied zwischen passt und passt nicht in den Speicher. → [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze)
+:::
+::: {#gloss:cvrptw}
**CVRPTW**`\index{CVRPTW}`{=latex} — *Capacitated Vehicle Routing Problem with Time Windows* — Tourenplanung mit einer Flotte, Kapazitätsgrenzen je Fahrzeug und Zeitfenstern je Kunde. Die praxisnahe Variante des VRP und Standardfall der Routing-Bibliothek von OR-Tools. → [Abschnitt 8.5](#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern)
+:::
+::: {#gloss:cvxpy}
**CVXPY**`\index{CVXPY}`{=latex} — Modellierungssprache für konvexe Optimierung. Sie schreibt das Problem in mathematiknaher Notation, prüft die Konvexität nach den Regeln der Disciplined Convex Programming und reicht es an einen passenden Solver weiter. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
## D
+::: {#gloss:data-snooping}
**Data Snooping**`\index{Data Snooping}`{=latex} — Wiederholtes Ausprobieren von Strategien auf denselben Daten, bis eine gut aussieht. Der Fund ist dann eine Eigenschaft der Stichprobe, nicht des Marktes — gefährlicher als ein Programmierfehler, weil das Ergebnis plausibel wirkt. → [Abschnitt 21.6](#sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens)
+:::
+::: {#gloss:dcp-disciplined-convex-programming}
+**DCP (Disciplined Convex Programming)**`\index{DCP}`{=latex} — Regelwerk von CVXPY, das jede Variable als konkav, konvex oder affin ausweist und aus der Kombination der Bausteine die Konkavität/Konvexität der Zielfunktion und der Nebenbedingungen erschließt. [Kapitel 11](#kap-qp-nlp) erklärt es; ein `DCPError` meldet eine Verletzung. → [Kapitel 11](#kap-qp-nlp)
+:::
+
+::: {#gloss:dcperror}
**DCPError**`\index{DCPError}`{=latex} — Fehlermeldung von CVXPY, wenn ein Ausdruck die Regeln der *Disciplined Convex Programming* verletzt. Sie besagt nicht, dass das Problem unlösbar ist, sondern dass CVXPY die Konvexität nicht nachweisen kann — meist hilft eine äquivalente Umformung. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:deletion-filter}
**Deletion Filter**`\index{Deletion Filter}`{=latex} — Verfahren zur Eingrenzung von Unlösbarkeit. Es fragt nicht „ist *diese* Bedingung schuld?“, sondern „wird *diese* Bedingung für den Widerspruch überhaupt gebraucht?“, und findet dadurch auch dann eine minimale unlösbare Teilmenge, wenn sich mehrere Widersprüche überlagern. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:diskrete-rendite}
**Diskrete Rendite**`\index{Diskrete Rendite}`{=latex} — Einfache Rendite $(P_t - P_{t-1})/P_{t-1}$. Sie ist **über Titel** additiv und deshalb die richtige Wahl für Portfoliogewichte — anders als die logarithmische Rendite, die über die Zeit additiv ist. → [Abschnitt 18.3](#sec:finanzdaten-diskrete-und-logarithmische-renditen)
+:::
+::: {#gloss:diversifikation}
**Diversifikation**`\index{Diversifikation}`{=latex} — Risikoreduktion durch Verteilung auf Titel, deren Renditen nicht vollständig gleichlaufen. Der Effekt steckt vollständig in den Kovarianzen: Bei Korrelation 1 verschwindet er, bei negativer Korrelation ist er am größten. → [Abschnitt 19.3](#sec:markowitz-warum-diversifikation-funktioniert)
+:::
+::: {#gloss:dto-data-transfer-object}
**DTO (Data Transfer Object)** — Schlichtes Objekt, das nur Daten transportiert und keine Logik enthält. Im Buch trägt die `Loesung`-Klasse in `or_kern.py` das Ergebnis eines Solverlaufs so aus der Rechenschicht heraus. → [Abschnitt 3.5](#sec:oekosystem-ein-system-vier-programmieransaetze)
+:::
+::: {#gloss:dual-simplex}
**Dual Simplex**`\index{Dual Simplex}`{=latex} — Variante des Simplex-Verfahrens, die eine dual zulässige Basis beibehält und die primale Zulässigkeit herstellt. Das Verfahren der Wahl beim Warm-Start, etwa in jedem Knoten eines Branch-and-Bound-Baums. → [Abschnitt 3.3](#sec:oekosystem-die-zwei-schichten-architektur)
+:::
+::: {#gloss:duales-problem}
**Duales Problem**`\index{Duales Problem}`{=latex} — Das jedem Optimierungsproblem zugeordnete Gegenstück, dessen Variablen die Nebenbedingungen des Originals bepreisen. Seine Optimalwerte sind die Schattenpreise; im Optimum stimmen beide Zielfunktionswerte überein. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:dualitaetstheorie}
**Dualitätstheorie**`\index{Dualitätstheorie}`{=latex} — Theorie des Zusammenhangs zwischen einem Optimierungsproblem und seinem Dualproblem. Der schwache Dualitätssatz liefert stets eine Schranke, der starke besagt, dass bei linearen Programmen beide Optima zusammenfallen. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:dynamische-programmierung-dp}
**Dynamische Programmierung (DP)**`\index{Dynamische Programmierung}`{=latex} — Lösungsprinzip für mehrstufige Entscheidungen: Das Problem wird in Zustände und Stufen zerlegt und rückwärts gelöst, wobei jeder Zustand nur einmal bewertet wird. Voraussetzung ist das Optimalitätsprinzip. → [Abschnitt 13.3](#sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip)
+:::
## E
+::: {#gloss:ecke-extrempunkt}
**Ecke (Extrempunkt)**`\index{Ecke (Extrempunkt)}`{=latex} — Punkt des zulässigen Bereichs, der sich nicht als echte Mischung zweier anderer zulässiger Punkte schreiben lässt. Nach dem Fundamentalsatz der linearen Optimierung liegt in einer Ecke stets ein Optimum — deshalb sucht der Simplex nur dort. → [Abschnitt 2.4](#sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder)
+:::
+::: {#gloss:effizienzgrenze-efficient-frontier}
**Effizienzgrenze (Efficient Frontier)**`\index{Effizienzgrenze}`{=latex} — Kurve aller Portfolios, die zu gegebenem Risiko die höchste erwartete Rendite liefern. Portfolios darunter sind dominiert, Portfolios darüber unerreichbar. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:entartung}
**Entartung**`\index{Entartung}`{=latex} — Zustand, in dem mehr Nebenbedingungen aktiv sind, als Variablen frei bleiben. Der Simplex kann dann Basiswechsel ohne Zielwertverbesserung durchführen, und die Schattenpreise sind nicht mehr eindeutig — zwei Solver dürfen verschiedene, gleichermaßen korrekte Dualwerte melden. → [Abschnitt 5.9](#sec:lp-entartung)
+:::
+::: {#gloss:entscheidungsvariable}
**Entscheidungsvariable**`\index{Entscheidungsvariable}`{=latex} — Die vom Solver frei wählbare Größe eines Modells. Ihr Wertebereich — kontinuierlich, ganzzahlig oder binär — bestimmt die Problemklasse und damit den Lösungsaufwand. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:entscheidungsvektor}
**Entscheidungsvektor**`\index{Entscheidungsvektor}`{=latex} — Zusammenfassung aller Entscheidungsvariablen zu einem Vektor $\mathbf{x}$. Erst diese Schreibweise macht die kompakte Matrixform eines Modells möglich. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:entweder-oder-bedingung}
**Entweder-Oder-Bedingung**`\index{Entweder-Oder-Bedingung}`{=latex} — Modellierungsmuster für sich ausschließende Alternativen: Genau eine von mehreren Bedingungen soll gelten. Umgesetzt über Binärvariablen mit Summe 1 und Big-M-Kopplung an die jeweilige Bedingung. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:error-maximizer-effekt}
**Error-Maximizer-Effekt**`\index{Error-Maximizer-Effekt}`{=latex} — Eigenschaft der Mean-Variance-Optimierung, Schätzfehler zu verstärken statt auszugleichen. Der Optimierer sucht gezielt die Richtungen mit der niedrigsten geschätzten Varianz — und das sind genau jene, deren Varianz am stärksten unterschätzt wurde. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:evpi-expected-value-of-perfect-information}
**EVPI (Expected Value of Perfect Information)**`\index{EVPI}`{=latex} — Differenz zwischen den erwarteten Kosten unter Unsicherheit und den Kosten bei perfektem Wissen. Obergrenze für den Wert jeder Prognoseverbesserung: Keine noch so gute Vorhersage kann mehr einbringen. → [Abschnitt 12.5](#sec:unsicherheit-zweistufige-stochastische-programmierung)
+:::
+::: {#gloss:exklusiv-oder}
**Exklusiv-Oder**`\index{Exklusiv-Oder}`{=latex} — Modellierungsmuster für „genau eine von zwei Optionen“: $y_A + y_B = 1$. Die Abgrenzung zum einfachen Oder ($y_A + y_B \ge 1$) ist in der Praxis eine häufige Fehlerquelle. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:expected-shortfall}
**Expected Shortfall**`\index{Expected Shortfall}`{=latex} — Anderer Name für den Conditional Value at Risk; in der Bankenregulierung die gebräuchliche Bezeichnung. Beide meinen den erwarteten Verlust jenseits des VaR-Quantils. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:explainable-or}
**Explainable OR**`\index{Explainable OR}`{=latex} — Nachvollziehbarmachung von Solver-Ergebnissen über Schattenpreise, aktive Restriktionen, Constraint-Trace und Kostenzerlegung. Das Gegenstück zu Explainable AI für Optimierungsmodelle. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
## F
+::: {#gloss:fallback-strategie}
**Fallback-Strategie**`\index{Fallback-Strategie}`{=latex} — Festgelegtes Vorgehen für den Fall, dass der Solver kein Ergebnis liefert: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Niemals: nichts. → [Abschnitt 22.5](#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform)
+:::
+::: {#gloss:falsche-dualwerte}
**Falsche Dualwerte**`\index{Falsche Dualwerte (Fehlerbild)}`{=latex} — Fehlerbild, bei dem Schattenpreise unplausibel wirken. Zwei Ursachen dominieren: Entartung macht sie mehrdeutig, und ganzzahlige Modelle haben streng genommen gar keine Dualwerte — was ein MILP-Solver zurückgibt, gehört zur Relaxation des letzten Knotens. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:fat-tails}
**Fat Tails**`\index{Fat Tails}`{=latex} — Verteilungsränder, die dicker auslaufen als bei der Normalverteilung. Extremereignisse sind dann deutlich häufiger als das Normalmodell vorhersagt — der Grund, warum varianzbasierte Risikomaße das Verlustrisiko unterschätzen. → [Abschnitt 20.3](#sec:cvar-die-zwei-schwaechen-des-markowitz-modells)
+:::
+::: {#gloss:fluch-der-dimensionalitaet}
**Fluch der Dimensionalität**`\index{Fluch der Dimensionalität}`{=latex} — Exponentielles Wachstum des Zustandsraums mit jeder zusätzlichen Zustandsdimension. Es begrenzt die dynamische Programmierung und motiviert näherungsweise Verfahren. → [Abschnitt 13.5](#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet)
+:::
+::: {#gloss:fluch-des-durchschnitts-flaw-of-averages}
**Fluch des Durchschnitts (Flaw of Averages)**`\index{Fluch des Durchschnitts}`{=latex} — Systematischer Fehler beim Planen mit Erwartungswerten statt mit Verteilungen. Das Ergebnis der Durchschnittsrechnung ist im Allgemeinen nicht der Durchschnitt der Ergebnisse; formal folgt das aus der Jensenschen Ungleichung. → [Abschnitt 12.3](#sec:unsicherheit-der-fluch-des-durchschnitts)
+:::
+::: {#gloss:flusserhaltung}
**Flusserhaltung**`\index{Flusserhaltung}`{=latex} — Bedingung, dass an jedem Knoten Abfluss minus Zufluss dem Saldo des Knotens entspricht — das Kirchhoff-Gesetz der Netzwerkoptimierung. Summieren sich die Salden nicht zu null, ist das Problem grundsätzlich unlösbar. → [Abschnitt 8.3](#sec:graphen-graphen-als-modellsprache)
+:::
+::: {#gloss:fundamentalsatz-der-linearen-optimierung}
**Fundamentalsatz der linearen Optimierung**`\index{Fundamentalsatz der linearen Optimierung}`{=latex} — Das Optimum eines lösbaren linearen Programms wird stets in mindestens einer Ecke des zulässigen Polyeders angenommen. Die Rechtfertigung dafür, nur endlich viele Ecken statt unendlich vieler Punkte zu prüfen. → [Abschnitt 2.4](#sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder)
+:::
## G
+::: {#gloss:gemischt-ganzzahlige-optimierung-milp}
**Gemischt-ganzzahlige Optimierung (MILP)**`\index{MILP}`{=latex} — *Mixed-Integer Linear Programming* — lineares Modell mit mindestens einer ganzzahligen oder binären Variablen. Die Ganzzahligkeit macht das Problem NP-schwer; gelöst wird es mit Branch-and-Cut. → [Abschnitt 6.3](#sec:milp-warum-runden-fundamental-scheitert)
+:::
+::: {#gloss:genetischer-algorithmus}
**Genetischer Algorithmus**`\index{Genetischer Algorithmus}`{=latex} — Metaheuristik, die eine Population von Lösungen durch Selektion, Rekombination und Mutation weiterentwickelt. Sie gibt keine Optimalitätsgarantie, kommt dafür mit Zielfunktionen zurecht, die weder linear noch differenzierbar sind. → [Abschnitt 9.8](#sec:metaheuristiken-abgrenzung)
+:::
+::: {#gloss:gestaffelte-preise}
**Gestaffelte Preise**`\index{Gestaffelte Preise (stückweise linear)}`{=latex} — Modellierungsmuster für Mengenrabatte und Tarifstufen. Die Menge wird in Segmente zerlegt, jedes mit eigenem Preis; Binärvariablen erzwingen, dass ein Segment erst genutzt wird, wenn das vorhergehende ausgeschöpft ist. Ohne diese Reihenfolgebedingung sucht sich der Solver einfach das billigste Segment heraus. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:gil-global-interpreter-lock}
**GIL (Global Interpreter Lock)** — Sperre des CPython-Interpreters, die verhindert, dass zwei Threads gleichzeitig Python-Code ausführen. Sie ist der Grund, warum echte Parallelität in Python über Prozesse statt Threads läuft — Solver umgehen sie, weil sie in C++ rechnen. → **Notation und Abkürzungen**
+:::
+::: {#gloss:gini-koeffizient}
**Gini-Koeffizient**`\index{Gini-Koeffizient}`{=latex} — Maß für die Ungleichverteilung einer Größe zwischen 0 (alle gleich) und 1 (einer bekommt alles). Im Buch als Fairnesskennzahl für Wunscherfüllungsquoten verwendet. → **Projektwerkstatt**
+:::
+::: {#gloss:gleitendes-fenster}
**Gleitendes Fenster**`\index{Gleitendes Fenster}`{=latex} — Auswertung, bei der ein Zeitfenster fester Länge über die Daten geschoben wird. Grundlage rollierender Schätzungen und des Walk-Forward-Backtests. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:global-minimum-variance-portfolio-gmv}
**Global Minimum Variance Portfolio (GMV)**`\index{GMV (Global Minimum Variance)}`{=latex} — Portfolio kleinstmöglicher Varianz. Es benötigt **keine Renditeprognose** und ist deshalb deutlich robuster gegen Schätzfehler als das renditemaximierende Mean-Variance-Portfolio. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:globale-constraints}
**Globale Constraints**`\index{Globale Constraints}`{=latex} — Vorgefertigte Bausteine wie `AllDifferent`, `NoOverlap` oder `Cumulative`, die häufige Strukturen kompakt ausdrücken. Ihr eigentlicher Wert sind die spezialisierten Propagatoren, die weit mehr Werte ausschließen als die gleichwertige Menge einzelner Ungleichungen. → [Abschnitt 7.4](#sec:cpsat-globale-constraints-die-bausteine-von-cp)
+:::
+::: {#gloss:globales-minimum}
**Globales Minimum**`\index{Globales Minimum}`{=latex} — Der kleinste Zielfunktionswert im gesamten zulässigen Bereich. Bei konvexen Problemen ist jedes lokale Minimum zugleich global — der Grund, warum Konvexität die Grenze zwischen leicht und schwer markiert. → [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)
+:::
+::: {#gloss:glop}
**GLOP** — *Google Linear Optimization Package* — der LP-Solver in OR-Tools. Er löst rein kontinuierliche Probleme; sobald Ganzzahligkeit ins Spiel kommt, übernimmt CP-SAT oder ein MILP-Backend. → **Notation und Abkürzungen**
+:::
+::: {#gloss:goldene-regel-des-backtestens}
**Goldene Regel des Backtestens**`\index{Goldene Regel des Backtestens}`{=latex} — Zum Zeitpunkt $t$ darf nur Information verwendet werden, die zu $t$ vorlag. Der Selbsttest mit absichtlich manipulierter Zukunft prüft das automatisch: Wird der Backtest dadurch besser, liegt ein Lookahead-Bias vor. → [Abschnitt 21.12](#sec:handelsmaschine-zusammenfassung)
+:::
+::: {#gloss:gradient}
**Gradient**`\index{Gradient}`{=latex} — Vektor der partiellen Ableitungen einer Funktion; er zeigt in Richtung des steilsten Anstiegs. Gradientenbasierte Verfahren wie SLSQP nutzen ihn, um sich schrittweise an ein Optimum heranzutasten. → [Abschnitt 11.2](#sec:qp-nlp-lernziele)
+:::
+::: {#gloss:graphen}
+**Graphen**`\index{Graphen}`{=latex} — Mathematische Struktur aus Knoten und Kanten; das Modellierungsmittel für Netzwerke, Touren und Flüsse. [Kapitel 8](#kap-graphen) behandelt Min-Cost-Flow, Matching und das Vehicle Routing Problem als Graphanwendungen. → [Kapitel 8](#kap-graphen)
+:::
+
+::: {#gloss:gurobi}
**Gurobi**`\index{Gurobi}`{=latex} — Kommerzieller Hochleistungssolver für LP, MILP und QP. Im Buch nicht vorausgesetzt, aber als Referenzpunkt genannt: Über die Modellierungsschicht lässt er sich meist durch Ändern eines einzigen Arguments einsetzen. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
## H
+::: {#gloss:herfindahl-index}
**Herfindahl-Index**`\index{Herfindahl-Index}`{=latex} — Summe der quadrierten Anteile; Maß für Konzentration. Sein Kehrwert ist die *effektive Titelzahl* und beantwortet, auf wie viele gleich große Positionen ein Portfolio praktisch verteilt ist. → **Projektwerkstatt**
+:::
+::: {#gloss:hierarchische-relaxation}
**Hierarchische Relaxation**`\index{Hierarchische Relaxation}`{=latex} — Gegenmittel gegen Unlösbarkeit im Betrieb: Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure Schlupfvariable. Der Solver liefert dann immer einen Plan und zeigt zugleich präzise, wo es klemmt. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:highs}
**HiGHS**`\index{HiGHS}`{=latex} — Offener Hochleistungssolver für LP, MILP und QP. Backend von `scipy.optimize.linprog` und CVXPY, direkt ansprechbar über `highspy`. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
## I
+::: {#gloss:iis-irreducible-infeasible-subset}
**IIS (Irreducible Infeasible Subset)**`\index{IIS (Irreducible Infeasible Subset)}`{=latex} — Minimale Menge von Nebenbedingungen, die zusammen unlösbar sind, deren echte Teilmengen aber alle lösbar wären. Das präziseste Ergebnis, das eine Unlösbarkeitsdiagnose liefern kann. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:implikation}
**Implikation**`\index{Implikation (Modellierungsmuster)}`{=latex} — Modellierungsmuster für „wenn A, dann B“: $y_B \ge y_A$. Die Umkehrung gilt dabei ausdrücklich nicht — $B$ darf auch ohne $A$ gesetzt sein. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:importfehler-zwischen-ortools-und-highspy}
**Importfehler zwischen ortools und highspy**`\index{Importfehler (ortools und highspy)}`{=latex} — Absturz beim Import beider Bibliotheken im selben Prozess: Sie bringen je eine eigene HiGHS-Kopie mit, deren C++-Symbole kollidieren. Abhilfe ist die Trennung in getrennte Prozesse — CVXPY zieht ein installiertes `highspy` dabei ungefragt mit hinein. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:infeasibility}
**Infeasibility**`\index{Infeasibility}`{=latex} — Zustand eines Modells ohne jede zulässige Lösung. Im Betrieb über Schlupfvariablen mit hohem Strafgewicht aufzufangen, in der Diagnose über den Deletion Filter einzugrenzen. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:instabile-loesung}
**Instabile Lösung**`\index{Instabile Lösung (Fehlerbild)}`{=latex} — Fehlerbild, bei dem winzige Datenänderungen die Lösung völlig umwerfen. Meist eine Folge schlechter Konditionierung; die Konditionszahl zeigt es an, die Ruiz-Equilibrierung behebt es. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:interior-point-verfahren}
**Interior-Point-Verfahren**`\index{Interior-Point-Verfahren}`{=latex} — Lösungsverfahren, das sich durch das Innere des zulässigen Bereichs zum Optimum bewegt, statt Ecken abzulaufen. Bei sehr großen Modellen oft schneller als der Simplex, liefert dafür keine Basislösung und damit weniger brauchbare Sensitivitätsinformationen. → [Abschnitt 3.3](#sec:oekosystem-die-zwei-schichten-architektur)
+:::
+::: {#gloss:intervallvariable}
**Intervallvariable**`\index{Intervallvariable}`{=latex} — Variable mit Start, Dauer und Ende, die in CP-SAT eine Aktivität beschreibt. Sie ist die Voraussetzung für Überschneidungsverbote und Ressourcenprofile. → [Abschnitt 7.4](#sec:cpsat-globale-constraints-die-bausteine-von-cp)
+:::
## J
+::: {#gloss:jensensche-ungleichung}
**Jensensche Ungleichung**`\index{Jensensche Ungleichung}`{=latex} — Für konvexe Funktionen gilt $\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])$. Die formale Grundlage des Fluchs des Durchschnitts: Mit Mittelwerten zu rechnen unterschätzt systematisch die erwarteten Kosten. → [Abschnitt 12.3](#sec:unsicherheit-der-fluch-des-durchschnitts)
+:::
+::: {#gloss:job-shop-scheduling}
**Job-Shop-Scheduling**`\index{Job-Shop-Scheduling}`{=latex} — Ablaufplanung, bei der mehrere Aufträge in je eigener Reihenfolge über dieselben Maschinen laufen. Klassisches Anwendungsfeld von CP-SAT; Zielgröße ist meist der Makespan. → [Abschnitt 7.6](#sec:cpsat-intervallvariablen-job-shop-scheduling)
+:::
## K
+::: {#gloss:kanonische-standardform}
**Kanonische Standardform**`\index{Kanonische Standardform}`{=latex} — Einheitliche Matrixschreibweise eines LP als $\min \mathbf{c}^\top\mathbf{x}$ unter den Bedingungen $\mathbf{A}\mathbf{x} \le \mathbf{b}$ und $\mathbf{x} \ge 0$. Jedes lineare Modell lässt sich darauf bringen — Solver erwarten genau diese Form. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:kapazitaetsvektor}
**Kapazitätsvektor**`\index{Kapazitätsvektor}`{=latex} — Der Vektor $\mathbf{b}$ der rechten Seiten in $\mathbf{A}\mathbf{x} \le \mathbf{b}$. Seine Einträge sind die verfügbaren Ressourcen, und die zugehörigen Schattenpreise sagen, was eine zusätzliche Einheit davon wert wäre. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:kardinalitaetsbeschraenkung}
**Kardinalitätsbeschränkung**`\index{Kardinalitätsbeschränkung}`{=latex} — Obergrenze für die Anzahl gleichzeitig aktiver Entscheidungen, formuliert über die Summe der zugehörigen Binärvariablen. Typisch für „höchstens zehn Titel im Depot“. → [Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)
+:::
+::: {#gloss:karush-kuhn-tucker-bedingungen-kkt}
**Karush-Kuhn-Tucker-Bedingungen (KKT)**`\index{KKT-Bedingungen}`{=latex} — Notwendige Optimalitätsbedingungen restringierter Probleme: Stationarität, primale und duale Zulässigkeit sowie komplementärer Schlupf. Bei konvexen Problemen sind sie zugleich hinreichend und damit ein vollständiger Optimalitätsnachweis. → [Abschnitt 11.4](#sec:qp-nlp-die-karush-kuhn-tucker-bedingungen)
+:::
+::: {#gloss:kohaerentes-risikomass}
**Kohärentes Risikomaß**`\index{Kohärentes Risikomaß}`{=latex} — Risikomaß mit den vier Eigenschaften Monotonie, Subadditivität, positive Homogenität und Translationsinvarianz. Der CVaR erfüllt sie, der VaR nicht — dessen Verletzung der Subadditivität kann Diversifikation rechnerisch bestrafen. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:kombinatorische-explosion}
**Kombinatorische Explosion**`\index{Kombinatorische Explosion}`{=latex} — Überproportionales Wachstum der Zahl möglicher Lösungen mit der Problemgröße. Sie ist der Grund, warum systematisches Ausprobieren schon bei kleinen Instanzen aussichtslos wird. → [Abschnitt 1.4](#sec:einfuehrung-warum-ausprobieren-scheitert-mit-eigener-rechnung)
+:::
+::: {#gloss:komplementaerer-schlupf}
**Komplementärer Schlupf**`\index{Komplementärer Schlupf}`{=latex} — Bedingung $s_i \cdot y_i = 0$: Eine Nebenbedingung ist entweder aktiv (kein Schlupf) oder ihr Schattenpreis verschwindet. Wirtschaftlich gelesen: Nur knappe Ressourcen haben einen Preis. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:konditionszahl}
**Konditionszahl**`\index{Konditionszahl}`{=latex} — Verhältnis von größtem zu kleinstem Singulärwert einer Matrix. Sie misst, wie stark kleine Datenänderungen die Lösung verzerren können; hohe Werte kündigen numerische Probleme an, lange bevor der Solver etwas meldet. → [Abschnitt 18.6](#sec:finanzdaten-praxis-datenpipeline-mit-korrekter-spaltenreihenfolge)
+:::
+::: {#gloss:konische-optimierung}
**Konische Optimierung**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} — Erweiterung der linearen Optimierung auf Kegelbedingungen. Der Second-Order Cone (SOCP) erfasst Normen und damit Risikoschranken, die semidefinite Variante (SDP) Bedingungen an Matrizen. Beide bleiben konvex und damit global lösbar. → [Abschnitt 12.7](#sec:unsicherheit-chance-constraints)
+:::
+::: {#gloss:konvexitaet}
**Konvexität**`\index{Konvexität}`{=latex} — Eigenschaft, bei der jede Verbindungsstrecke zweier Punkte innerhalb der Menge bzw. unterhalb des Funktionsgraphen liegt. Bei konvexen Problemen ist jedes lokale Optimum global — die eigentliche Grenze zwischen leicht und schwer lösbar. → [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)
+:::
+::: {#gloss:korn-transformation}
**Korn-Transformation**`\index{Korn-Transformation}`{=latex} — Umformung der nicht-konvexen Sharpe-Ratio-Maximierung in ein konvexes quadratisches Programm durch Homogenisierung. Sie macht aus einem Bruch eine Skalierung und damit aus einem unlösbaren ein lösbares Problem. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:kostenzerlegung}
**Kostenzerlegung**`\index{Kostenzerlegung}`{=latex} — Aufschlüsselung eines Zielfunktionswerts in seine Bestandteile — etwa Alpha-Ertrag, Risikoprämie und Transaktionskostenstrafe. Sie beantwortet die Frage „woraus besteht dieses Ergebnis?“ und ist ein Baustein der Erklärbarkeit. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:kovarianzmatrix}
**Kovarianzmatrix**`\index{Kovarianzmatrix}`{=latex} — Matrix der paarweisen Kovarianzen aller Titel. Sie steuert im Markowitz-Modell den Diversifikationseffekt und ist bei vielen Titeln und wenigen Beobachtungen notorisch schlecht konditioniert. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:kumulative-ressource}
**Kumulative Ressource**`\index{Kumulative Ressource}`{=latex} — Ressource mit begrenzter Kapazität, die mehrere Aktivitäten gleichzeitig nutzen können — etwa vier Monteure. In CP-SAT über `AddCumulative` ausgedrückt. → [Anhang B](#anhang-modellierungsmuster)
+:::
## L
+::: {#gloss:lagrange-funktion}
**Lagrange-Funktion**`\index{Lagrange-Funktion}`{=latex} — Zielfunktion zuzüglich der mit Multiplikatoren gewichteten Nebenbedingungen. Ihre Stationaritätsbedingung ist die erste der KKT-Bedingungen und die Brücke zwischen restringierter und freier Optimierung. → [Abschnitt 11.4](#sec:qp-nlp-die-karush-kuhn-tucker-bedingungen)
+:::
+::: {#gloss:lagrange-multiplikator}
**Lagrange-Multiplikator**`\index{Lagrange-Multiplikator}`{=latex} — Gewicht, mit dem eine Nebenbedingung in die Lagrange-Funktion eingeht. Sein Optimalwert ist der Schattenpreis der Bedingung. → [Abschnitt 11.4](#sec:qp-nlp-die-karush-kuhn-tucker-bedingungen)
+:::
+::: {#gloss:large-neighborhood-search-lns}
**Large Neighborhood Search (LNS)**`\index{Large Neighborhood Search}`{=latex} — Metaheuristik, die wiederholt einen größeren Teil der aktuellen Lösung zerstört und exakt neu aufbaut. Sie verbindet die Reichweite heuristischer Sprünge mit der Genauigkeit eines Solvers auf dem Teilproblem. → [Abschnitt 9.7](#sec:metaheuristiken-lns)
+:::
+::: {#gloss:laufzeitexplosion}
**Laufzeitexplosion**`\index{Laufzeitexplosion (Fehlerbild)}`{=latex} — Fehlerbild, bei dem ein Modell nach einer kleinen Erweiterung nicht mehr fertig rechnet. Häufige Ursachen sind ein zu großes $M$, fehlende Symmetriebrechung und eine schwache Relaxation. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:ledoit-wolf-shrinkage}
**Ledoit-Wolf-Shrinkage**`\index{Ledoit-Wolf-Shrinkage}`{=latex} — Schrumpfung der Stichprobenkovarianz in Richtung eines strukturierten Ziels; der optimale Mischungsfaktor wird analytisch bestimmt. `scikit-learn` verwendet die skalierte Einheitsmatrix als Ziel. → [Abschnitt 18.5](#sec:finanzdaten-ledoit-wolf-shrinkage)
+:::
+::: {#gloss:lexikografische-optimierung}
**Lexikografische Optimierung**`\index{Lexikografische Optimierung}`{=latex} — Mehrzielverfahren mit strenger Rangfolge: Das wichtigste Ziel wird optimiert, sein Wert festgeschrieben, dann das zweitwichtigste — und so fort. Kein Ziel darf zugunsten eines nachrangigen verschlechtert werden. → [Abschnitt 14.7](#sec:mehrziel-epsilon)
+:::
+::: {#gloss:lineare-programmierung-lp}
**Lineare Programmierung (LP)**`\index{Lineare Programmierung (LP)}`{=latex} — *Linear Programming* — Optimierung einer linearen Zielfunktion unter linearen Nebenbedingungen mit kontinuierlichen Variablen. Die am besten verstandene Problemklasse des Operations Research: in Polynomialzeit lösbar und mit vollständiger Dualitätstheorie. → [Abschnitt 5.3](#sec:lp-die-standardform-und-schlupfvariablen)
+:::
+::: {#gloss:linopy}
**Linopy**`\index{Linopy}`{=latex} — Modellierungsbibliothek, die Optimierungsmodelle über beschriftete Achsen aufbaut (`xarray`-Stil). Ihre Stärke sind große, regelmäßig indizierte Modelle wie Energiesystemrechnungen. → [Abschnitt 3.7](#sec:oekosystem-pyomo-linopy)
+:::
+::: {#gloss:linprog}
**linprog**`\index{linprog}`{=latex} — Funktion `scipy.optimize.linprog` zum Lösen linearer Programme. Sie erwartet das Modell in kanonischer Standardform als Matrizen und benutzt seit SciPy 1.11 standardmäßig HiGHS als Backend. → [Abschnitt 5.7](#sec:lp-die-vorzeichenfalle-bei-schattenpreisen)
+:::
+::: {#gloss:logarithmische-rendite}
**Logarithmische Rendite**`\index{Logarithmische Rendite}`{=latex} — Stetige Rendite als Logarithmus des Preisverhältnisses. Sie ist **über die Zeit** additiv und deshalb die richtige Wahl für Zeitreihenaggregation — nicht aber für die Gewichtung im Portfolio. → [Abschnitt 18.3](#sec:finanzdaten-diskrete-und-logarithmische-renditen)
+:::
+::: {#gloss:lokales-minimum}
**Lokales Minimum**`\index{Lokales Minimum}`{=latex} — Punkt, der in seiner Umgebung optimal ist, global aber übertroffen werden kann. Bei nichtkonvexen Problemen liefern gradientenbasierte Verfahren nur solche Punkte; ein Multistart macht das Problem sichtbar. → [Abschnitt 2.5](#sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und)
+:::
+::: {#gloss:lookahead-bias}
**Lookahead-Bias**`\index{Lookahead-Bias}`{=latex} — Fehler, bei dem Informationen in eine Entscheidung einfließen, die zum Entscheidungszeitpunkt noch nicht vorlagen. Die häufigste Ursache für Backtests, die zu gut aussehen, um wahr zu sein. → [Abschnitt 21.6](#sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens)
+:::
+::: {#gloss:lp-relaxation}
**LP-Relaxation**`\index{LP-Relaxation}`{=latex} — Das ganzzahlige Modell ohne die Ganzzahligkeitsforderung. Ihre Lösung ist schnell zu berechnen und liefert eine Schranke: Besser als der Relaxationswert kann die ganzzahlige Lösung nicht werden. Genau darauf beruht das Abschneiden im Branch-and-Bound. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
## M
+::: {#gloss:makespan}
**Makespan**`\index{Makespan}`{=latex} — Gesamtdurchlaufzeit eines Ablaufplans: der Zeitpunkt, zu dem die letzte Aktivität endet. Standardzielgröße im Job-Shop-Scheduling. → [Abschnitt 7.6](#sec:cpsat-intervallvariablen-job-shop-scheduling)
+:::
+::: {#gloss:mape}
**MAPE** — *Mean Absolute Percentage Error* — mittlerer absoluter prozentualer Prognosefehler. Beliebt, weil einheitenfrei, aber unbrauchbar bei Werten nahe null. → [Abschnitt 15.3](#sec:prognose-naht)
+:::
+::: {#gloss:marchenko-pastur-gesetz}
**Marchenko-Pastur-Gesetz**`\index{Marchenko-Pastur-Gesetz}`{=latex} — Ergebnis der Zufallsmatrizentheorie über die Eigenwertverteilung von Stichproben-Kovarianzmatrizen. Es zeigt, dass die kleinsten Eigenwerte systematisch unterschätzt und die größten überschätzt werden — die theoretische Wurzel des Error-Maximizer-Effekts. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:markowitz-modell-mean-variance}
**Markowitz-Modell (Mean-Variance)**`\index{Markowitz-Modell (Mean-Variance)}`{=latex} — Rahmenwerk, das ein Portfolio über das Zusammenspiel von erwarteter Rendite und Kovarianz bewertet. Es begründete die moderne Portfoliotheorie und ist zugleich das Lehrbeispiel für die Empfindlichkeit gegenüber Schätzfehlern. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:marktauswirkung-market-impact}
**Marktauswirkung (Market Impact)**`\index{Marktauswirkung}`{=latex} — Preisverschlechterung, die eine eigene Order durch ihr Volumen auslöst. Sie wächst überproportional mit der Handelsgeschwindigkeit und ist der Grund, große Orders über die Zeit zu strecken. → [Abschnitt 13.4](#sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung)
+:::
+::: {#gloss:matplotlib}
**Matplotlib** — Standardbibliothek für statische Diagramme in Python. Im Buch erzeugt sie alle Abbildungen der Druckfassung; die interaktiven Fassungen der Website stammen von Plotly. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:matrixform}
**Matrixform**`\index{Matrixform}`{=latex} — Kompakte Schreibweise eines Modells über Vektoren und Matrizen statt einzelner Gleichungen. Sie ist nicht nur kürzer, sondern die Form, die Solver tatsächlich entgegennehmen. → [Abschnitt 2.12](#sec:fundament-zusammenfassung)
+:::
+::: {#gloss:matrix-vektor-produkt}
**Matrix-Vektor-Produkt**`\index{Matrix-Vektor-Produkt}`{=latex} — Rechenoperation $\mathbf{A}\mathbf{x}$, die jede Zeile der Matrix mit dem Vektor multipliziert. In der Optimierung berechnet sie den Ressourcenverbrauch aller Nebenbedingungen in einem Schritt. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:maximin-fairness}
**Maximin-Fairness**`\index{Maximin-Fairness}`{=latex} — Zielsetzung „der am schlechtesten Gestellte soll möglichst gut dastehen“. Modelliert über eine Hilfsvariable $t$ mit $t \le f_i(\mathbf{x})$ für alle $i$ und Maximierung von $t$. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:maximum-drawdown}
**Maximum Drawdown**`\index{Maximum Drawdown}`{=latex} — Größter prozentualer Rückgang vom bisherigen Höchststand. Anders als die Volatilität misst er den tatsächlich erlittenen Verlustweg und ist deshalb die Kennzahl, an der Anleger aussteigen. → [Abschnitt 21.5](#sec:handelsmaschine-die-engine)
+:::
+::: {#gloss:mensch-in-der-schleife}
**Mensch in der Schleife**`\index{Mensch in der Schleife}`{=latex} — Betriebsprinzip, bei dem der Optimierer **vorschlägt** und ein Mensch **entscheidet** — zumindest in der Einführungsphase. Das baut Vertrauen auf und fängt Modellfehler ab, bevor sie wirksam werden. → [Abschnitt 22.5](#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform)
+:::
+::: {#gloss:metaheuristik}
**Metaheuristik**`\index{Metaheuristik}`{=latex} — Übergeordnetes Suchschema wie Simulated Annealing, Tabu-Suche oder genetische Algorithmen, das auf viele Problemtypen anwendbar ist. Es liefert gute Lösungen ohne Optimalitätsbeweis und ist dort angebracht, wo exakte Verfahren an der Größe scheitern. → [Abschnitt 9.3](#sec:metaheuristiken-aufgabe)
+:::
+::: {#gloss:min-cost-flow-problem}
**Min-Cost-Flow-Problem**`\index{Min-Cost-Flow}`{=latex} — Kostengünstigster Transport durch ein Netzwerk unter Kapazitäts- und Flusserhaltungsbedingungen. Die Modellmatrix ist total unimodular, weshalb schon die LP-Lösung ganzzahlig ausfällt. → [Abschnitt 8.3](#sec:graphen-graphen-als-modellsprache)
+:::
+::: {#gloss:mindestabnahmemenge}
**Mindestabnahmemenge**`\index{Mindestabnahmemenge}`{=latex} — Bedingung „entweder gar nichts oder mindestens $m$“. Sie ist nicht linear und braucht eine Binärvariable: $m\,y \le x \le M\,y$. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:minlp}
**MINLP**`\index{MINLP}`{=latex} — *Mixed-Integer Nonlinear Programming* — Modelle mit ganzzahligen Variablen **und** nichtlinearen Funktionen. Die schwierigste der Standardklassen: Sie vereint die kombinatorische Explosion mit dem Problem lokaler Optima. → [Abschnitt 11.6](#sec:qp-nlp-nichtkonvex)
+:::
+::: {#gloss:mip-gap}
**MIP-Gap**`\index{MIP-Gap}`{=latex} — Relativer Abstand zwischen der besten gefundenen Lösung und der besten bekannten Schranke. Er beziffert, wie viel im schlechtesten Fall noch fehlt — ein Gap von 2 % heißt: höchstens 2 % besser ist überhaupt möglich. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:miqp}
**MIQP** — *Mixed-Integer Quadratic Programming* — quadratische Zielfunktion mit ganzzahligen Variablen. Der Fall, der entsteht, sobald ein Portfoliomodell eine Obergrenze für die Zahl der Titel bekommt. → **Notation und Abkürzungen**
+:::
+::: {#gloss:monte-carlo-simulation}
**Monte-Carlo-Simulation**`\index{Monte-Carlo-Simulation}`{=latex} — Erzeugung vieler Zufallsszenarien, um Kennzahlen empirisch zu schätzen. Sie **bewertet** eine gegebene Entscheidung, optimiert aber nicht — der Unterschied wird in der Praxis häufig übersehen. → [Abschnitt 12.4](#sec:unsicherheit-monte-carlo-simulation)
+:::
+::: {#gloss:mse}
**MSE** — *Mean Squared Error* — mittlerer quadratischer Fehler. Das Standardmaß der Prognosegüte; es bestraft große Abweichungen überproportional und ist nicht dasselbe wie geringe Entscheidungskosten. → **Notation und Abkürzungen**
+:::
+::: {#gloss:mtz-formulierung}
**MTZ-Formulierung**`\index{MTZ-Formulierung}`{=latex} — Miller-Tucker-Zemlin-Bedingungen, die über Rangvariablen Kurzzyklen ausschließen. Kompakt in der Zahl der Nebenbedingungen, dafür mit schwacher Relaxation — der klassische Zielkonflikt bei Tourenmodellen. → [Abschnitt 8.5](#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern)
+:::
+::: {#gloss:multi-objective-optimization}
**Multi-Objective Optimization**`\index{Multi-Objective Optimization}`{=latex} — Optimierung mit mehreren, konkurrierenden Zielen. Statt einer besten Lösung gibt es eine Pareto-Front; die Auswahl daraus ist eine Entscheidung, keine Rechnung. → [Abschnitt 22.9](#sec:praxisfallen-weiterfuehrende-literatur-und-roadmap)
+:::
## N
+::: {#gloss:nebenbedingung-constraint}
**Nebenbedingung (Constraint)**`\index{Nebenbedingung}`{=latex} — Gleichung oder Ungleichung, die zulässige von unzulässigen Lösungen trennt. Harte Bedingungen müssen erfüllt sein, weiche werden über Strafterme lediglich bepreist. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:newsvendor-problem}
**Newsvendor-Problem**`\index{Newsvendor-Problem}`{=latex} — Klassisches Einperiodenmodell: Wie viel bestellt man bei unsicherer Nachfrage, wenn Überbestand und Fehlmenge unterschiedlich teuer sind? Die Lösung ist ein Quantil des Nachfragemodells, nicht ihr Erwartungswert. → [Abschnitt 12.1](#sec:unsicherheit-schnellstart)
+:::
+::: {#gloss:nichtlineare-programmierung-nlp}
**Nichtlineare Programmierung (NLP)**`\index{Nichtlineare Programmierung (NLP)}`{=latex} — *Nonlinear Programming* — Problemklasse mit nichtlinearer Ziel- oder Nebenbedingungsfunktion. Ohne Konvexität liefern Verfahren wie SLSQP nur lokale Optima. → [Abschnitt 11.5](#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize)
+:::
+::: {#gloss:nichtueberlappung}
**Nichtüberlappung**`\index{Nichtüberlappung}`{=latex} — Bedingung, dass sich Aktivitäten auf derselben Ressource zeitlich nicht überschneiden. In CP-SAT über `AddNoOverlap` auf Intervallvariablen ausgedrückt. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:np-schwer}
**NP-schwer**`\index{NP-schwer}`{=latex} — Komplexitätsklasse von Problemen, für die kein Algorithmus mit polynomialer Laufzeit bekannt ist. Praktisch heißt das: Die Lösungszeit kann mit der Problemgröße explodieren — nicht aber, dass jede Instanz schwer wäre. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
+::: {#gloss:numpy}
**NumPy** — Grundbibliothek für numerisches Rechnen in Python. Sie liefert das Array, mit dem Vektoren und Matrizen eines Modells überhaupt erst kompakt und schnell aufgebaut werden können. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
## O
+::: {#gloss:operations-research-or}
**Operations Research (OR)**`\index{Operations Research}`{=latex} — Disziplin, die reale Entscheidungsprobleme in mathematische Modelle überführt und mit exakten oder heuristischen Algorithmen löst. Sie beantwortet nicht „was wird geschehen?“, sondern „was sollen wir tun?“. → [Abschnitt 1.3](#sec:einfuehrung-was-ist-operations-research-wirklich)
+:::
+::: {#gloss:optimalitaetsprinzip}
**Optimalitätsprinzip**`\index{Optimalitätsprinzip}`{=latex} — Grundsatz von Bellman: Jede Teilpolitik einer optimalen Politik ist ihrerseits optimal für den erreichten Zustand. Ohne ihn wäre die Rückwärtsinduktion nicht zulässig. → [Abschnitt 13.3](#sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip)
+:::
+::: {#gloss:or-tools}
**OR-Tools**`\index{OR-Tools}`{=latex} — Optimierungssammlung von Google mit CP-SAT, einer Routing-Bibliothek und Anbindungen an LP/MILP-Solver. Im Buch die erste Wahl für Reihenfolge-, Zuordnungs- und Tourenprobleme. → [Abschnitt 1.2](#sec:einfuehrung-lernziele)
+:::
+::: {#gloss:overfitting}
**Overfitting**`\index{Overfitting}`{=latex} — Anpassung eines Modells an Zufälligkeiten der Trainingsdaten statt an die zugrunde liegende Struktur. Im Backtesting die Regel, nicht die Ausnahme — deshalb der Walk-Forward-Test. → [Abschnitt 21.6](#sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens)
+:::
## P
+::: {#gloss:pandas}
**pandas** — Bibliothek für tabellarische Daten mit Zeit- und Beschriftungsindex. Im Buch der übliche Weg von Roh- zu Modelldaten, besonders in den Finanzkapiteln. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:parameter}
**Parameter**`\index{Parameter (Modell)}`{=latex} — Feststehende Größe eines Modells — Kosten, Kapazitäten, Bedarfe. Parameter werden nicht optimiert, sondern gegeben; ihre Unsicherheit ist der Gegenstand der stochastischen und robusten Optimierung. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:pareto-front}
**Pareto-Front**`\index{Pareto-Front}`{=latex} — Menge aller Lösungen, bei denen kein Ziel verbessert werden kann, ohne ein anderes zu verschlechtern. Sie zeigt den Zielkonflikt als Kurve, statt ihn in einer Gewichtung zu verstecken. → [Abschnitt 14.3](#sec:mehrziel-dominanz)
+:::
+::: {#gloss:pivotisierung}
**Pivotisierung**`\index{Pivotisierung}`{=latex} — Basiswechsel im Simplex-Verfahren: Eine Variable verlässt die Basis, eine andere tritt ein. Jeder Pivotschritt entspricht dem Weiterwandern zur benachbarten Ecke des Polyeders. → [Abschnitt 5.4](#sec:lp-der-simplex-algorithmus-schritt-fuer-schritt)
+:::
+::: {#gloss:plotly}
**Plotly** — Bibliothek für interaktive Diagramme im Browser. Die Website des Buchs bindet damit Gantt-Diagramme, Effizienzlinien und Pareto-Fronten zum Zoomen und Erkunden ein. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:polars}
**Polars**`\index{Polars}`{=latex} — Schnelle DataFrame-Bibliothek mit spaltenorientiertem Speicher und verzögerter Auswertung. Im Buch als Alternative zu pandas dort erwähnt, wo der Modellaufbau selbst zum Engpass wird. → [Abschnitt 3.8](#sec:oekosystem-vektorisierung)
+:::
+::: {#gloss:polyeder}
**Polyeder**`\index{Polyeder}`{=latex} — Schnittmenge endlich vieler Halbräume — die geometrische Gestalt des zulässigen Bereichs eines linearen Programms. Seine Ecken sind die Kandidaten für das Optimum. → [Abschnitt 2.4](#sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder)
+:::
+::: {#gloss:positiv-semi-definit}
**Positiv (semi-)definit**`\index{Positiv (semi-)definit}`{=latex} — Eigenschaft einer symmetrischen Matrix, deren Eigenwerte alle $> 0$ (definit) bzw. $\ge 0$ (semidefinit) sind. Definit bedeutet streng konvex und eine eindeutige Lösung, semidefinit konvex mit möglicherweise mehreren. → [Abschnitt 11.3](#sec:qp-nlp-das-quadratische-programm-qp)
+:::
+::: {#gloss:praeskriptive-analytik}
**Präskriptive Analytik**`\index{Präskriptive Analytik}`{=latex} — Analysestufe, die vorschreibt, welche Handlung unter den gegebenen Bedingungen die beste ist. Sie folgt auf die deskriptive (was war?) und die prädiktive Stufe (was wird?). → [Abschnitt 1.3](#sec:einfuehrung-was-ist-operations-research-wirklich)
+:::
+::: {#gloss:predict-then-optimize}
**Predict-then-Optimize**`\index{Predict-then-Optimize}`{=latex} — Zweistufiges Vorgehen: erst prognostizieren, dann mit der Prognose optimieren. Der Bruch liegt dazwischen — das Prognosemodell minimiert Vorhersagefehler, nicht Entscheidungskosten. → [Abschnitt 15.3](#sec:prognose-naht)
+:::
+::: {#gloss:primales-problem}
**Primales Problem**`\index{Primales Problem}`{=latex} — Das ursprünglich formulierte Optimierungsproblem, im Gegensatz zu seinem Dualproblem. Welches von beiden „primal“ heißt, ist Konvention: Das Duale des Dualen ist wieder das Primale. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:pydantic}
**pydantic** — Bibliothek zur Validierung von Datenstrukturen anhand von Typannotationen. Im Buch prüft sie die Eingaben des Optimierungsdienstes, bevor ein Modell überhaupt gebaut wird. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:pymoo}
**pymoo** — Bibliothek für mehrkriterielle Optimierung mit evolutionären Verfahren. Sie liefert Pareto-Fronten dort, wo sich die Ziele nicht sinnvoll skalarisieren lassen. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:pyomo}
**Pyomo**`\index{Pyomo}`{=latex} — Umfangreiche Modellierungssprache für Optimierung in Python, mit Unterstützung für abstrakte Modelle, viele Solver und nichtlineare Formulierungen. Mächtiger, aber ausführlicher als CVXPY. → [Abschnitt 3.7](#sec:oekosystem-pyomo-linopy)
+:::
## Q
+::: {#gloss:q-learning}
**Q-Learning**`\index{Q-Learning}`{=latex} — Verfahren des bestärkenden Lernens, das den Wert von Zustand-Aktion-Paaren aus Erfahrung schätzt, ohne ein Modell der Umgebung zu kennen. Verwandt mit der dynamischen Programmierung, aber lernend statt rechnend. → [Abschnitt 13.5](#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet)
+:::
+::: {#gloss:quadratische-programmierung-qp}
**Quadratische Programmierung (QP)**`\index{Quadratische Programmierung (QP)}`{=latex} — *Quadratic Programming* — Optimierung einer quadratischen Zielfunktion unter linearen Nebenbedingungen. Bei positiv semidefiniter Matrix konvex und damit zuverlässig global lösbar; die Portfoliooptimierung ist der Standardfall. → [Abschnitt 11.3](#sec:qp-nlp-das-quadratische-programm-qp)
+:::
## R
+::: {#gloss:rebalancing}
**Rebalancing**`\index{Rebalancing}`{=latex} — Periodische Rückführung des Portfolios auf die Zielgewichte. Zu häufig kostet Gebühren, zu selten lässt die Gewichte driften — und die Termine müssen Handelstage sein. → [Abschnitt 21.4](#sec:handelsmaschine-rebalancing-termine-richtig-bestimmen)
+:::
+::: {#gloss:regime-shift}
**Regime-Shift**`\index{Regime-Shift}`{=latex} — Strukturbruch in den Daten, nach dem historisch geschätzte Momente ihre Gültigkeit verlieren. Kein Modellfehler, sondern eine Eigenschaft der Welt — abzufangen über kürzere Schätzfenster und robuste Formulierungen. → [Abschnitt 22.3](#sec:praxisfallen-die-fuenf-typischen-praxisfallen)
+:::
+::: {#gloss:reinforcement-learning}
**Reinforcement Learning**`\index{Reinforcement Learning}`{=latex} — Lernen einer Entscheidungsstrategie aus Rückmeldungen der Umgebung. Es löst dieselbe Klasse mehrstufiger Probleme wie die dynamische Programmierung, benötigt aber kein explizites Übergangsmodell. → [Abschnitt 13.5](#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet)
+:::
+::: {#gloss:relaxation}
**Relaxation**`\index{Relaxation}`{=latex} — Absichtliches Weglassen einschränkender Forderungen — typischerweise der Ganzzahligkeit —, um eine schnell berechenbare Schranke zu gewinnen. Der Wert der Relaxation begrenzt, wie gut die echte Lösung überhaupt werden kann. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
+::: {#gloss:robuste-optimierung}
**Robuste Optimierung**`\index{Robuste Optimierung}`{=latex} — Auslegung auf den ungünstigsten Fall innerhalb einer Unsicherheitsmenge. Sie verlangt keine Wahrscheinlichkeiten, sondern nur eine Angabe darüber, wie weit die Parameter schwanken können. → [Abschnitt 12.6](#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case)
+:::
+::: {#gloss:rockafellar-uryasev-theorem}
**Rockafellar-Uryasev-Theorem**`\index{Rockafellar-Uryasev-Theorem}`{=latex} — Ergebnis, das die CVaR-Minimierung in eine konvexe Hilfsfunktion überführt. Es macht die Optimierung des Tail-Risikos mit gewöhnlichen linearen Solvern möglich. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:routing-bibliothek}
**Routing-Bibliothek**`\index{Routing-Bibliothek}`{=latex} — Spezialisierter Teil von OR-Tools für Touren- und Reihenfolgeprobleme. Sie arbeitet heuristisch mit Zeitbudget — „optimal“ wird hier in der Regel nicht bewiesen. → [Abschnitt 8.10](#sec:graphen-zusammenfassung)
+:::
+::: {#gloss:rucksackproblem}
**Rucksackproblem**`\index{Rucksackproblem}`{=latex} — Auswahl von Gegenständen mit Wert und Gewicht unter einer Kapazitätsgrenze. Das Schulbeispiel der ganzzahligen Optimierung und Modell für jede Budgetauswahl. → [Abschnitt 6.6](#sec:milp-beispiel-das-rucksackproblem)
+:::
+::: {#gloss:rueckwaertsinduktion}
**Rückwärtsinduktion**`\index{Rückwärtsinduktion}`{=latex} — Lösungsrichtung der dynamischen Programmierung: von der letzten Stufe rückwärts. Nur dort steht der Wert eines Zustands ohne Kenntnis der Zukunft fest, und von dort aus lässt sich jede frühere Stufe auf bereits bekannte Werte zurückführen. → [Abschnitt 13.2](#sec:dynamische-programmierung-lernziele)
+:::
+::: {#gloss:ruiz-equilibrierung}
**Ruiz-Equilibrierung**`\index{Ruiz-Equilibrierung}`{=latex} — Skalierungsverfahren, das Zeilen und Spalten einer Matrix iterativ so multipliziert, dass alle Beträge in derselben Größenordnung liegen. Nach wenigen Durchläufen liegen die Maxima bei 1 — die einfachste wirksame Gegenmaßnahme gegen schlechte Konditionierung. → [Abschnitt 2.7](#sec:fundament-kondition)
+:::
+::: {#gloss:ruestzeit}
**Rüstzeit**`\index{Rüstzeit}`{=latex} — Umschaltzeit zwischen zwei aufeinanderfolgenden Aufträgen auf derselben Maschine. Sie ist reihenfolgeabhängig und macht aus einer einfachen Zuordnung ein Reihenfolgeproblem. → [Anhang B](#anhang-modellierungsmuster)
+:::
## S
+::: {#gloss:sat-boolean-satisfiability}
**SAT (Boolean Satisfiability)**`\index{SAT (Boolean Satisfiability)}`{=latex} — Problem, für eine aussagenlogische Formel eine erfüllende Belegung zu finden. Das erste als NP-vollständig bewiesene Problem — und die Sprache, in die CP-SAT jedes Modell übersetzt. → [Abschnitt 7.3](#sec:cpsat-ein-anderes-denkmodell)
+:::
+::: {#gloss:satz-von-birkhoff-und-von-neumann}
**Satz von Birkhoff und von Neumann**`\index{Satz von Birkhoff und von Neumann}`{=latex} — Jede doppelt stochastische Matrix ist eine Mischung von Permutationsmatrizen. Er erklärt, warum das Zuordnungsproblem schon als lineares Programm ganzzahlige Lösungen liefert. → [Abschnitt 8.4](#sec:graphen-bipartites-matching-das-zuordnungsproblem)
+:::
+::: {#gloss:schattenpreis}
**Schattenpreis**`\index{Schattenpreis}`{=latex} — Optimalwert einer Dualvariablen: Um wie viel ändert sich der Zielwert, wenn die zugehörige Ressource um eine Einheit erweitert wird? Er gilt nur lokal — innerhalb des Bereichs, den die Sensitivitätsanalyse ausweist. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:schaetzfehler}
**Schätzfehler**`\index{Schätzfehler}`{=latex} — Abweichung geschätzter Größen wie Renditen oder Kovarianzen von ihren wahren Werten. In der Portfoliooptimierung nicht ein Randproblem, sondern das Hauptproblem. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:scheduling}
+**Scheduling**`\index{Scheduling}`{=latex} — Zuweisung von Tätigkeiten an Maschinen oder Personen unter Ressourcen- und Reihenfolgebedingungen. [Kapitel 7](#kap-cpsat) behandelt es mit CP-SAT (Intervallvariablen), [Kapitel 9](#kap-metaheuristiken) mit Simulated Annealing und LNS. → [Kapitel 7](#kap-cpsat)
+:::
+
+::: {#gloss:schlupfvariable}
**Schlupfvariable**`\index{Schlupfvariable}`{=latex} — Nichtnegative Hilfsvariable, die eine Ungleichung in eine Gleichung überführt. Ihr Wert zeigt die ungenutzte Reserve; ist er null, ist die Bedingung bindend. → [Abschnitt 5.3](#sec:lp-die-standardform-und-schlupfvariablen)
+:::
+::: {#gloss:schnittebenen-cutting-planes}
**Schnittebenen (Cutting Planes)**`\index{Schnittebenen}`{=latex} — Zusätzliche gültige Ungleichungen, die gebrochene LP-Lösungen abschneiden, ohne einen einzigen ganzzahligen Punkt zu verlieren. Sie verschärfen die Relaxation und verkleinern damit den Suchbaum. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
+::: {#gloss:scikit-learn}
**scikit-learn** — Bibliothek für maschinelles Lernen. Im Buch für Prognosemodelle und für die Ledoit-Wolf-Shrinkage der Kovarianzmatrix eingesetzt. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:scip}
**SCIP**`\index{SCIP}`{=latex} — Akademischer Solver für gemischt-ganzzahlige lineare und nichtlineare Programme, über OR-Tools und Pyomo ansprechbar. Für Forschung und Lehre frei nutzbar; kommerzieller Einsatz verlangt eine eigene Lizenz. → [Abschnitt 6.4](#sec:milp-branch-and-bound)
+:::
+::: {#gloss:scipy}
**SciPy** — Wissenschaftliche Standardbibliothek von Python. Ihr Teilmodul `scipy.optimize` enthält mit `linprog`, `milp` und `minimize` die Einstiegswerkzeuge für lineare, ganzzahlige und nichtlineare Optimierung. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
+::: {#gloss:semikontinuierliche-variable}
**Semikontinuierliche Variable**`\index{Semikontinuierliche Variable}`{=latex} — Variable, die entweder null ist oder in einem Intervall $[m, M]$ liegt. Sie erfasst Mindestlosgrößen und wird über eine Binärvariable modelliert. → [Abschnitt 6.5](#sec:milp-modellierungstricks-big-m-und-logische-bedingungen)
+:::
+::: {#gloss:sensitivitaetsanalyse}
**Sensitivitätsanalyse**`\index{Sensitivitätsanalyse}`{=latex} — Untersuchung, in welchem Bereich sich Koeffizienten und Kapazitäten ändern dürfen, ohne die Struktur der Optimallösung zu verändern. Sie macht aus einer Zahl eine Aussage über ihre Belastbarkeit. → [Abschnitt 5.8](#sec:lp-praxisfall-sensitivitaetsanalyse-mit-korrekten-schattenpreisen)
+:::
+::: {#gloss:shannon-entropie}
**Shannon-Entropie**`\index{Shannon-Entropie}`{=latex} — Maß für die Gleichverteiltheit einer Verteilung. Als Zielfunktion eingesetzt erzwingt sie Streuung, ohne eine bestimmte Aufteilung vorzugeben. → [Abschnitt 11.5](#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize)
+:::
+::: {#gloss:sharpe-ratio}
**Sharpe Ratio**`\index{Sharpe-Ratio}`{=latex} — Überrendite über den risikofreien Zins je Einheit Volatilität. Ihre Maximierung ist zunächst nicht konvex, wird es aber durch die Korn-Transformation. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:simplex-algorithmus}
**Simplex-Algorithmus**`\index{Simplex-Algorithmus}`{=latex} — Verfahren von Dantzig, das von Ecke zu Ecke des Polyeders wandert und den Zielfunktionswert dabei monoton verbessert. Im schlechtesten Fall exponentiell, in der Praxis außerordentlich zuverlässig. → [Abschnitt 5.4](#sec:lp-der-simplex-algorithmus-schritt-fuer-schritt)
+:::
+::: {#gloss:simulated-annealing}
**Simulated Annealing**`\index{Simulated Annealing}`{=latex} — Metaheuristik nach dem Vorbild des Metallabkühlens: Verschlechterungen werden anfangs häufig, später kaum noch akzeptiert. So entkommt die Suche lokalen Optima, ohne die Richtung ganz zu verlieren. → [Abschnitt 9.5](#sec:metaheuristiken-annealing)
+:::
+::: {#gloss:skalarisierung}
**Skalarisierung**`\index{Skalarisierung}`{=latex} — Zusammenfassung mehrerer Ziele zu einer gewichteten Summe. Bequem, aber mit zwei Haken: Die Gewichte sind Werturteile, und nicht jeder Punkt der Pareto-Front lässt sich so erreichen. → [Abschnitt 14.4](#sec:mehrziel-skalarisierung)
+:::
+::: {#gloss:skalarprodukt}
**Skalarprodukt**`\index{Skalarprodukt}`{=latex} — Summe der paarweisen Produkte zweier Vektoren, $\mathbf{c}^\top\mathbf{x}$. In der Optimierung die kompakte Schreibweise jeder linearen Zielfunktion. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:slsqp}
**SLSQP**`\index{SLSQP}`{=latex} — *Sequential Least Squares Programming* — gradientenbasiertes Verfahren in `scipy.optimize.minimize` für nichtlineare Probleme mit Nebenbedingungen. Es findet lokale Optima; ohne Konvexität entscheidet der Startpunkt über das Ergebnis. → [Abschnitt 11.5](#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize)
+:::
+::: {#gloss:snapshot-prinzip}
**Snapshot-Prinzip**`\index{Snapshot-Prinzip}`{=latex} — Betriebsregel, nach der jeder Optimierungslauf auf einem unveränderlichen Datenschnappschuss mit eigener Kennung arbeitet. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar. → [Abschnitt 22.5](#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform)
+:::
+::: {#gloss:solver}
+**Solver**`\index{Solver}`{=latex} — Softwarekomponente, die ein mathematisches Modell löst — also das Optimierungsproblem in eine Lösung übersetzt. [Kapitel 3](#kap-oekosystem) vergleicht die in diesem Buch verwendeten Solver (HiGHS, OR-Tools/CP-SAT, CVXPY) und ihre Stärken. → [Kapitel 3](#kap-oekosystem)
+:::
+
+::: {#gloss:solver-status}
**Solver-Status**`\index{Solver-Status}`{=latex} — Rückgabewert, mit dem ein Solver das Ergebnis einordnet: `OPTIMAL` (bewiesen optimal), `FEASIBLE` (zulässig, aber unbewiesen), `INFEASIBLE` (keine zulässige Lösung), `UNBOUNDED` (Zielwert unbeschränkt) und `UNKNOWN` (Abbruch ohne Aussage). Ihn ungeprüft zu übergehen ist die häufigste Fehlerquelle im produktiven Einsatz. → **Notation und Abkürzungen**
+:::
+::: {#gloss:starker-dualitaetssatz}
**Starker Dualitätssatz**`\index{Starker Dualitätssatz}`{=latex} — Aussage, dass primales und duales Problem im Optimum denselben Zielfunktionswert besitzen — bei linearen Programmen immer, sobald beide lösbar sind. Er macht den Dualwert zu einem belastbaren Preis statt zu einer bloßen Schranke. → [Abschnitt 5.6](#sec:lp-dualitaet-und-schattenpreise)
+:::
+::: {#gloss:stichproben-kovarianzmatrix}
**Stichproben-Kovarianzmatrix**`\index{Stichproben-Kovarianzmatrix}`{=latex} — Aus historischen Daten geschätzte Kovarianzmatrix. Bei weniger Beobachtungen als Titeln ist sie singulär und damit nicht invertierbar; schon bei ähnlicher Größenordnung wird sie extrem instabil. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:stochastische-programmierung}
**Stochastische Programmierung**`\index{Stochastische Programmierung}`{=latex} — Optimierung unter Unsicherheit mit bekannten Wahrscheinlichkeiten. In der zweistufigen Form wird zwischen Entscheidungen vor und nach der Beobachtung unterschieden — genau der Unterschied, den der Fluch des Durchschnitts einebnet. → [Abschnitt 12.5](#sec:unsicherheit-zweistufige-stochastische-programmierung)
+:::
+::: {#gloss:strafkosten}
**Strafkosten**`\index{Strafkosten}`{=latex} — Kosten, mit denen die Verletzung einer weichen Bedingung in der Zielfunktion belegt wird. Ihre Höhe ist eine Modellierungsentscheidung: Sie legt fest, wie viel Regelverstoß was wert ist. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:subadditivitaet}
**Subadditivität**`\index{Subadditivität}`{=latex} — Eigenschaft $\rho(A+B) \le \rho(A)+\rho(B)$: Diversifikation darf das Risiko nicht erhöhen. Der VaR verletzt sie, weshalb er kein kohärentes Risikomaß ist. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:subtour}
**Subtour**`\index{Subtour}`{=latex} — Kurzzyklus, der nur einen Teil der Knoten enthält und das Depot umgeht. Ohne eigene Bedingungen — etwa MTZ — liefert ein Tourenmodell solche Gebilde als scheinbar optimale Lösung. → [Abschnitt 8.5](#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern)
+:::
+::: {#gloss:survivorship-bias}
**Survivorship-Bias**`\index{Survivorship-Bias}`{=latex} — Verzerrung durch die Auswahl nur derjenigen Titel, die es bis heute gibt. Die ausgeschiedenen fehlen — und mit ihnen die schlechtesten Verläufe. → [Abschnitt 21.6](#sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens)
+:::
+::: {#gloss:symmetriebrechung}
**Symmetriebrechung**`\index{Symmetriebrechung}`{=latex} — Zusätzliche Ordnungsbedingungen, die verhindern, dass der Solver gleichwertige Vertauschungen mehrfach durchsucht. Bei identischen Maschinen oder Fahrzeugen oft der größte einzelne Laufzeitgewinn. → [Anhang B](#anhang-modellierungsmuster)
+:::
## T
+::: {#gloss:tabu-suche}
**Tabu-Suche**`\index{Tabu-Suche}`{=latex} — Metaheuristik, die zuletzt besuchte Lösungen für einige Schritte sperrt. Die Sperrliste zwingt die Suche aus lokalen Optima heraus, statt zwischen zwei Nachbarn zu pendeln. → [Abschnitt 9.8](#sec:metaheuristiken-abgrenzung)
+:::
+::: {#gloss:tangentialportfolio}
**Tangentialportfolio**`\index{Tangentialportfolio}`{=latex} — Portfolio auf der Effizienzgrenze mit der höchsten Sharpe Ratio — der Berührpunkt der Kapitalmarktlinie. Es reagiert besonders empfindlich auf Renditeschätzfehler. → [Abschnitt 19.4](#sec:markowitz-das-mean-variance-modell)
+:::
+::: {#gloss:technologiematrix}
**Technologiematrix**`\index{Technologiematrix}`{=latex} — Die Matrix $\mathbf{A}$ der Verbrauchskoeffizienten. Ihr Eintrag $a_{ij}$ sagt, wie viel von Ressource $i$ eine Einheit von Aktivität $j$ verbraucht. Zusammen mit dem Kapazitätsvektor $\mathbf{b}$ bildet sie die Nebenbedingungen $\mathbf{A}\mathbf{x} \le \mathbf{b}$. → [Abschnitt 2.3](#sec:fundament-warum-ueberhaupt-vektoren-und-matrizen)
+:::
+::: {#gloss:totale-unimodularitaet}
**Totale Unimodularität**`\index{Totale Unimodularität}`{=latex} — Eigenschaft einer Matrix, bei der jede quadratische Teilmatrix die Determinante $0$, $+1$ oder $-1$ hat. Folge: Alle Ecken des Polyeders sind ganzzahlig — Ganzzahligkeit muss gar nicht gefordert werden. → [Abschnitt 8.4](#sec:graphen-bipartites-matching-das-zuordnungsproblem)
+:::
+::: {#gloss:transaktionskosten}
**Transaktionskosten**`\index{Transaktionskosten}`{=latex} — Beim Umschichten anfallende Kosten aus Gebühren, Spread und Marktauswirkung. Über eine $L_1$-Strafe modellierbar, die zugleich den Turnover begrenzt. → [Abschnitt 20.5](#sec:cvar-transaktionskosten-ueber-die-l-1-norm)
+:::
+::: {#gloss:trickle-flow}
**Trickle Flow**`\index{Trickle Flow}`{=latex} — Fehlerbild bei zu großem $M$: Ein verschwindend kleiner Schaltwert — etwa $4 \cdot 10^{-8}$ — gilt dem Solver als null, lässt aber multipliziert mit einem riesigen $M$ einen großen Fluss durch. Das Lager liefert, ohne offiziell geöffnet zu sein, und die Fixkosten fehlen in der Rechnung. → [Abschnitt A.6](#sec:loesungen-milp)
+:::
+::: {#gloss:tsp-traveling-salesperson-problem}
**TSP (Traveling Salesperson Problem)**`\index{TSP (Traveling Salesperson Problem)}`{=latex} — Suche nach der kürzesten Rundreise durch $n$ Orte. Das bekannteste NP-schwere Problem und der Ausgangspunkt aller Tourenmodelle. → [Abschnitt 8.5](#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern)
+:::
+::: {#gloss:turnover-umschlag}
**Turnover (Umschlag)**`\index{Turnover (Umschlag)}`{=latex} — Summe der absoluten Gewichtsänderungen einer Umschichtung. Die Größe, an der Transaktionskosten hängen — und die Kennzahl, mit der man Rebalancing-Regeln vergleicht. → [Abschnitt 20.5](#sec:cvar-transaktionskosten-ueber-die-l-1-norm)
+:::
+::: {#gloss:twap}
**TWAP**`\index{TWAP}`{=latex} — *Time-Weighted Average Price* — Ausführungsstrategie, die eine Order gleichmäßig über die Zeit verteilt. Einfache Vergleichsgröße für jede optimierte Ausführung. → [Abschnitt 13.4](#sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung)
+:::
## U
+::: {#gloss:umruestkosten}
**Umrüstkosten**`\index{Umrüstkosten (Modellierungsmuster)}`{=latex} — Kosten, die beim Wechsel zwischen zwei Produkten anfallen. Sie hängen vom Vorgänger ab und erzwingen deshalb Binärvariablen über Paare statt über einzelne Aufträge. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:unbounded}
**Unbounded**`\index{Unbounded (Fehlerbild)}`{=latex} — Zustand, in dem der Zielwert unbeschränkt verbessert werden kann. Fast immer ein Modellfehler — eine fehlende Kapazitätsgrenze oder ein vergessenes Vorzeichen —, nicht ein besonders gutes Ergebnis. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:ungarischer-algorithmus}
**Ungarischer Algorithmus**`\index{Ungarischer Algorithmus}`{=latex} — Kombinatorisches Verfahren für das Zuordnungsproblem mit polynomialer Laufzeit. Es löst exakt, was als allgemeines MILP deutlich teurer wäre. → [Abschnitt 8.10](#sec:graphen-zusammenfassung)
+:::
+::: {#gloss:unsicherheitsmenge}
**Unsicherheitsmenge**`\index{Unsicherheitsmenge}`{=latex} — Vorab definierter Bereich möglicher Parameterwerte, gegen dessen ungünstigstes Element eine robuste Lösung abgesichert wird. Ihre Größe steuert unmittelbar den Preis der Robustheit. → [Abschnitt 12.6](#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case)
+:::
+::: {#gloss:unsinniges-ergebnis}
**Unsinniges Ergebnis**`\index{Unsinniges Ergebnis (Fehlerbild)}`{=latex} — Fehlerbild, bei dem der Solver zwar `OPTIMAL` meldet, die Lösung aber offensichtlich unbrauchbar ist. Dann ist nicht die Rechnung falsch, sondern das Modell: Es beschreibt eine andere Wirklichkeit als die gemeinte. → [Anhang C](#anhang-fehlerdiagnose)
+:::
## V
+::: {#gloss:value-at-risk-var}
**Value at Risk (VaR)**`\index{Value at Risk (VaR)}`{=latex} — Verlustschwelle, die mit vorgegebener Wahrscheinlichkeit nicht überschritten wird. Sie sagt nichts über die Verlusthöhe dahinter und ist nicht subadditiv — beides Gründe für den Übergang zum CVaR. → [Abschnitt 20.4](#sec:cvar-value-at-risk-und-conditional-value)
+:::
+::: {#gloss:vehicle-routing-problem-vrp}
**Vehicle Routing Problem (VRP)**`\index{Vehicle Routing Problem (VRP)}`{=latex} — Verallgemeinerung des TSP auf mehrere Fahrzeuge mit Depot und Kapazitätsgrenzen. In der Variante mit Zeitfenstern (VRPTW) ist es der praktische Standardfall der Tourenplanung. Sich kreuzende Touren sind hier kein Fehler: Die Zeitfenster können die Reihenfolge erzwingen. → [Abschnitt 8.5](#sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern)
+:::
+::: {#gloss:verdaechtig-guter-backtest}
**Verdächtig guter Backtest**`\index{Verdächtig guter Backtest (Fehlerbild)}`{=latex} — Fehlerbild der Finanzkapitel: eine Strategie, die im Rückblick zu gut abschneidet. Die Prüfreihenfolge lautet Lookahead, Survivorship, Kosten, Data Snooping — in dieser Reihenfolge, weil sie nach Häufigkeit sortiert ist. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:verhaeltnis-bedingung}
**Verhältnis-Bedingung**`\index{Verhältnis-Bedingung}`{=latex} — Anforderung der Art „höchstens 30 % aus einer Quelle“. Sie wird linear, sobald man sie als $x_i \le 0{,}3 \sum_j x_j$ schreibt statt als Bruch. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:vertauschte-spalten}
**Vertauschte Spalten**`\index{Vertauschte Spalten (Fehlerbild)}`{=latex} — Fehlerbild, bei dem Daten und Modell unterschiedliche Reihenfolgen annehmen. Es rechnet widerspruchsfrei und liefert dennoch Unsinn — deshalb gehört die Spaltenreihenfolge abgesichert, nicht angenommen. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:volatilitaet}
**Volatilität**`\index{Volatilität}`{=latex} — Standardabweichung der Renditen, üblicherweise auf ein Jahr skaliert. Sie misst Schwankung in beide Richtungen und behandelt Gewinne damit wie Verluste. → [Abschnitt 18.3](#sec:finanzdaten-diskrete-und-logarithmische-renditen)
+:::
+::: {#gloss:vorrangbeziehung}
**Vorrangbeziehung**`\index{Vorrangbeziehung}`{=latex} — Bedingung „Aktivität A muss vor Aktivität B abgeschlossen sein“, formuliert als $\text{Ende}_A \le \text{Start}_B$. Der Grundbaustein jeder Ablaufplanung. → [Anhang B](#anhang-modellierungsmuster)
+:::
## W
+::: {#gloss:walk-forward-backtest}
**Walk-Forward-Backtest**`\index{Walk-Forward-Backtest}`{=latex} — Rollierende Auswertung, bei der Parameter stets nur auf Vergangenheitsdaten geschätzt und auf dem unmittelbar folgenden Zeitraum getestet werden. Die einzige Backtest-Bauform, die Lookahead strukturell ausschließt. → [Abschnitt 21.5](#sec:handelsmaschine-die-engine)
+:::
+::: {#gloss:warm-start}
**Warm-Start**`\index{Warm-Start}`{=latex} — Übergabe einer bekannten Lösung als Startpunkt. Bei wiederholten, ähnlichen Läufen — etwa täglicher Neuplanung — oft der größte Zeitgewinn überhaupt. → [Abschnitt 6.8](#sec:milp-gap)
+:::
+::: {#gloss:weiche-grenze-mit-strafkosten}
**Weiche Grenze mit Strafkosten**`\index{Weiche Grenze mit Strafkosten}`{=latex} — Modellierungsmuster, das eine Schranke überschreitbar macht und die Überschreitung über eine Schlupfvariable in der Zielfunktion bepreist. So bleibt das Modell lösbar und zeigt trotzdem, wo es klemmt. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:widerspruechliche-solver}
**Widersprüchliche Solver**`\index{Widersprüchliche Solver (Fehlerbild)}`{=latex} — Fehlerbild, bei dem zwei Solver verschiedene Lösungen melden. Bei gleichem Zielwert sind es Alternativoptima und beide haben recht; bei verschiedenem Zielwert liegt ein Modell- oder Toleranzproblem vor. → [Anhang C](#anhang-fehlerdiagnose)
+:::
+::: {#gloss:wurzel-zeit-regel}
**Wurzel-Zeit-Regel**`\index{Wurzel-Zeit-Regel}`{=latex} — Skalierung der Volatilität mit $\sqrt{T}$. Sie gilt streng nur für Standardabweichungen unabhängiger Größen ohne Drift — für VaR und CVaR ist sie lediglich eine grobe Näherung. → [Abschnitt 20.6](#sec:cvar-implementierung-cvar-portfolio-mit-reibung)
+:::
## Y
+::: {#gloss:yfinance}
**yfinance** — Bibliothek zum Abruf historischer Kursdaten. Die einzige Abhängigkeit des Buchs, die eine Internetverbindung braucht — vier Programme nutzen sie. → [Abschnitt 3.4](#sec:oekosystem-die-werkzeuge-im-vergleich)
+:::
## Z
+::: {#gloss:zielfunktion}
**Zielfunktion**`\index{Zielfunktion}`{=latex} — Der zu minimierende oder maximierende Ausdruck, der die Entscheidungsvariablen zu einer einzigen Bewertungszahl verdichtet. Ihre Formulierung entscheidet, was das Modell überhaupt für gut hält. → [Abschnitt 1.6](#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or)
+:::
+::: {#gloss:zufallsmatrizentheorie}
**Zufallsmatrizentheorie**`\index{Zufallsmatrizentheorie}`{=latex} — *Random Matrix Theory* — Theorie der Eigenwertverteilung zufälliger Matrizen. Sie liefert den Maßstab dafür, welche Struktur in einer geschätzten Kovarianzmatrix echt ist und welche bloßes Rauschen. → [Abschnitt 18.4](#sec:finanzdaten-das-schaetzfehler-problem)
+:::
+::: {#gloss:zulaessiger-bereich-feasible-region}
**Zulässiger Bereich (Feasible Region)**`\index{Zulässiger Bereich}`{=latex} — Menge aller Punkte, die sämtliche Nebenbedingungen gleichzeitig erfüllen. Ist sie leer, ist das Modell unlösbar; ist sie unbeschränkt, kann der Zielwert entgleiten. → [Anhang F](#anhang-literatur)
+:::
+::: {#gloss:zuordnungsproblem}
**Zuordnungsproblem**`\index{Zuordnungsproblem (1:1)}`{=latex} — Eins-zu-eins-Zuordnung zweier gleich großer Mengen mit minimalen Kosten. Dank totaler Unimodularität schon als LP ganzzahlig lösbar. → [Anhang B](#anhang-modellierungsmuster)
+:::
+::: {#gloss:zustandslosigkeit}
**Zustandslosigkeit**`\index{Zustandslosigkeit (OR-Plattform)}`{=latex} — Betriebsprinzip, nach dem der rechnende Dienst keinen Zustand hält: Eingabe und Ausgabe sind Daten. Das macht Skalierung und Wiederholung trivial. → [Abschnitt 22.5](#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform)
+:::
+::: {#gloss:zustandsraum}
**Zustandsraum**`\index{Zustandsraum}`{=latex} — Menge aller Zustände, die ein mehrstufiges Entscheidungsproblem annehmen kann. Seine Größe entscheidet, ob dynamische Programmierung praktikabel ist. → [Abschnitt 13.5](#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet)
+:::
+::: {#gloss:zwei-phasen-simplex-methode}
**Zwei-Phasen-Simplex-Methode**`\index{Zwei-Phasen-Simplex-Methode}`{=latex} — Vorgehen, um überhaupt eine erste zulässige Ecke zu finden. Phase 1 minimiert mit Hilfsvariablen die Verletzung der Nebenbedingungen; bleibt dabei ein Rest, ist das Modell unlösbar. Erst Phase 2 optimiert die eigentliche Zielfunktion. → [Abschnitt A.5](#sec:loesungen-lp)
+:::
+::: {#gloss:zweischichtige-architektur}
**Zweischichtige Architektur**`\index{Zweischichtige Architektur}`{=latex} — Trennung in eine Modellierungsschicht, in der man das Problem mathematiknah formuliert, und eine Solver-Schicht aus hochoptimierten C++-Bibliotheken. Man schreibt Optimierungsalgorithmen im modernen OR nicht selbst. → [Abschnitt 3.3](#sec:oekosystem-die-zwei-schichten-architektur)
+:::
---
@@ -28972,49 +29460,49 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 1](#kap-einfuehrung) und [Kapitel 2](#kap-fundament).*
-* ★★★ **Bertsimas, D. & Tsitsiklis, J. N. (1997):** *Introduction to Linear Optimization.* Athena Scientific, Belmont. [Lineare Optimierung] *Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, Sensitivitätsanalyse und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der Dualitätstheorie.*
-* ★★★ **Nocedal, J. & Wright, S. J. (2006):** *Numerical Optimization* (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] *Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und Ruiz-Equilibrierung.*
+* ★★★ **Bertsimas, D. & Tsitsiklis, J. N. (1997):** *Introduction to Linear Optimization.* Athena Scientific, Belmont. [Lineare Optimierung] *Standardwerk zur geometrischen und algebraischen Theorie des Simplex-Verfahrens, Dualität, [Sensitivitätsanalyse](#gloss:sensitivitaetsanalyse){.glossar-link} und Netzwerkflüssen. Alt, aber unverändert die klarste Herleitung der [Dualitätstheorie](#gloss:dualitaetstheorie){.glossar-link}.*
+* ★★★ **Nocedal, J. & Wright, S. J. (2006):** *Numerical Optimization* (2. Aufl.). Springer, New York. DOI: 10.1007/978-0-387-40065-5 [Numerik der Optimierung] *Warum Verfahren numerisch scheitern: Konditionierung, Schrittweitensteuerung, Konvergenzraten. Die Grundlage des Abschnitts über Skalierung und [Ruiz-Equilibrierung](#gloss:ruiz-equilibrierung){.glossar-link}.*
* ★★ **Hillier, F. S. & Lieberman, G. J. (2021):** *Introduction to Operations Research* (11. Aufl.). McGraw-Hill, New York. [OR-Überblick] *Breite Einführung inklusive Warteschlangen, Entscheidungsmodellen und Simulation — die Themen, die dieses Buch bewusst auslässt.*
* ★★ **Kallrath, J. (2021):** *Business Optimization Using Mathematical Programming* (2. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-73237-0 [Modellierungspraxis] *Der Brückenschlag von der betrieblichen Fragestellung zum Modell, mit ausführlichem Katalog industrieller Anwendungsfälle. Nächster Nachbar zu [Kapitel 4](#kap-modellierung).*
-* ★ **Dantzig, G. B. (1963):** *Linear Programming and Extensions.* Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] *Das Originalwerk des Erfinders des Simplex-Algorithmus. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.*
+* ★ **Dantzig, G. B. (1963):** *Linear Programming and Extensions.* Princeton University Press. DOI: 10.1515/9781400884179 [Historisch] *Das Originalwerk des Erfinders des [Simplex-Algorithmus](#gloss:simplex-algorithmus){.glossar-link}. Historisch bedeutend, für die tägliche Arbeit heute entbehrlich.*
## Modellierung in Python: Bibliotheken und Modellierungsschichten
*Zu [Kapitel 3](#kap-oekosystem).*
-* ★★★ **Diamond, S. & Boyd, S. (2016):** *CVXPY: A Python-embedded modeling language for convex optimization.* In: *Journal of Machine Learning Research* 17(83), S. 1–5. [Bibliothek: CVXPY] *Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. [www.cvxpy.org](https://www.cvxpy.org)*
+* ★★★ **Diamond, S. & Boyd, S. (2016):** *[CVXPY](#gloss:cvxpy){.glossar-link}: A Python-embedded modeling language for convex optimization.* In: *Journal of Machine Learning Research* 17(83), S. 1–5. [Bibliothek: CVXPY] *Die Referenz zur im Buch am häufigsten benutzten Modellierungsschicht. [www.cvxpy.org](https://www.cvxpy.org)*
* ★★ **Agrawal, A., Verschueren, R., Diamond, S. & Boyd, S. (2018):** *A rewriting system for convex optimization problems.* In: *Journal of Control and Decision* 5(1), S. 42–60. DOI: 10.1080/23307706.2017.1397554 [Bibliothek: CVXPY] *Erklärt, was CVXPY beim Übersetzen tatsächlich tut — und warum ein `DCPError` kein Solverfehler ist.*
-* ★★★ **Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):** *Pyomo — Optimization Modeling in Python* (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] *Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.*
-* ★★ **Hofmann, F. (2023):** *Linopy: Linear optimization with n-dimensional labeled variables.* In: *Journal of Open Source Software* 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] *Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.*
-* ★★ **Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):** *SciPy 1.0: fundamental algorithms for scientific computing in Python.* In: *Nature Methods* 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] *Die zitierfähige Quelle zu `scipy.optimize` — dem Einstiegspunkt in [Kapitel 5](#kap-lp).*
-* ★★ **Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):** *Array programming with NumPy.* In: *Nature* 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] *Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.*
+* ★★★ **Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P. & Woodruff, D. L. (2021):** *[Pyomo](#gloss:pyomo){.glossar-link} — Optimization Modeling in Python* (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-030-68928-5 [Bibliothek: Pyomo] *Das Referenzbuch der Autoren zur mächtigsten der drei im Buch verglichenen Modellierungsschichten.*
+* ★★ **Hofmann, F. (2023):** *[Linopy](#gloss:linopy){.glossar-link}: Linear optimization with n-dimensional labeled variables.* In: *Journal of Open Source Software* 8(84), 4823. DOI: 10.21105/joss.04823 [Bibliothek: linopy] *Beschreibt den beschrifteten-Achsen-Ansatz und enthält den Speicher- und Geschwindigkeitsvergleich gegen Pyomo, PuLP, CVXPY und gurobipy. Frei zugänglich.*
+* ★★ **Virtanen, P., Gommers, R., Oliphant, T. E. et al. (2020):** *[SciPy](#gloss:scipy){.glossar-link} 1.0: fundamental algorithms for scientific computing in Python.* In: *Nature Methods* 17, S. 261–272. DOI: 10.1038/s41592-019-0686-2 [Bibliothek: SciPy] *Die zitierfähige Quelle zu `scipy.optimize` — dem Einstiegspunkt in [Kapitel 5](#kap-lp).*
+* ★★ **Harris, C. R., Millman, K. J., van der Walt, S. J. et al. (2020):** *Array programming with [NumPy](#gloss:numpy){.glossar-link}.* In: *Nature* 585, S. 357–362. DOI: 10.1038/s41586-020-2649-2 [Bibliothek: NumPy] *Grundlage jeder vektorisierten Modellerzeugung; siehe den Abschnitt über den Aufbau großer Modelle.*
## Lineare und ganzzahlige Optimierung
*Zu [Kapitel 5](#kap-lp) und [Kapitel 6](#kap-milp).*
-* ★★★ **Wolsey, L. A. (2020):** *Integer Programming* (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] *Branch-and-Bound, Branch-and-Cut, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines $M$ besser ist als ein großes.*
+* ★★★ **Wolsey, L. A. (2020):** *Integer Programming* (2. Aufl.). Wiley, Hoboken. DOI: 10.1002/9781119606475 [Ganzzahlige Optimierung] *[Branch-and-Bound](#gloss:branch-and-bound){.glossar-link}, [Branch-and-Cut](#gloss:branch-and-cut){.glossar-link}, Polyedertheorie, Formulierungsstärke. Die Begründung dafür, warum ein kleines $M$ besser ist als ein großes.*
* ★★★ **Conforti, M., Cornuéjols, G. & Zambelli, G. (2014):** *Integer Programming.* Springer, Cham. DOI: 10.1007/978-3-319-11008-0 [Ganzzahlige Optimierung] *Modernere Darstellung als Wolsey, mit ausführlicher Behandlung von Schnittebenen und totaler Unimodularität.*
* ★★ **Klotz, E. & Newman, A. M. (2013):** *Practical guidelines for solving difficult mixed integer linear programs.* In: *Surveys in Operations Research and Management Science* 18(1–2), S. 18–32. DOI: 10.1016/j.sorms.2012.12.001 [Ganzzahlige Optimierung] *Was zu tun ist, wenn ein Modell nicht fertig rechnet — Skalierung, Formulierung, Symmetrie. Die Literaturgrundlage von [Anhang C](#anhang-fehlerdiagnose).*
-* ★★ **Achterberg, T. (2009):** *SCIP: solving constraint integer programs.* In: *Mathematical Programming Computation* 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [Solver: SCIP] *Beschreibt die Verbindung von MILP und Constraint Programming in einem Solver.*
+* ★★ **Achterberg, T. (2009):** *[SCIP](#gloss:scip){.glossar-link}: solving constraint integer programs.* In: *Mathematical Programming Computation* 1(1), S. 1–41. DOI: 10.1007/s12532-008-0001-1 [[Solver](#gloss:solver){.glossar-link}: SCIP] *Beschreibt die Verbindung von [MILP](#gloss:gemischt-ganzzahlige-optimierung-milp){.glossar-link} und Constraint Programming in einem Solver.*
## Moderne Solver: Implementierung, Vergleich und Benchmarks
*Zu [Kapitel 3](#kap-oekosystem) und [Anhang D](#anhang-spickzettel). Neuer Abschnitt: Das Buch vergleicht durchgehend Solver, die Literatur dazu stand bisher verstreut.*
-* ★★★ **Huangfu, Q. & Hall, J. A. J. (2018):** *Parallelizing the dual revised simplex method.* In: *Mathematical Programming Computation* 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: HiGHS] *Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von `scipy.optimize.linprog` und CVXPY. [highs.dev](https://highs.dev)*
+* ★★★ **Huangfu, Q. & Hall, J. A. J. (2018):** *Parallelizing the dual revised simplex method.* In: *Mathematical Programming Computation* 10(1), S. 119–142. DOI: 10.1007/s12532-017-0130-5 [Solver: [HiGHS](#gloss:highs){.glossar-link}] *Die vom HiGHS-Projekt selbst benannte Zitierquelle. HiGHS ist das Backend von `scipy.optimize.linprog` und CVXPY. [highs.dev](https://highs.dev)*
* ★★★ **Gleixner, A., Hendel, G., Gamrath, G. et al. (2021):** *MIPLIB 2017: data-driven compilation of the 6th mixed-integer programming library.* In: *Mathematical Programming Computation* 13(3), S. 443–490. DOI: 10.1007/s12532-020-00194-3 [Benchmarks] *Die Testmenge, auf der praktisch jeder Solververgleich beruht: 1065 Instanzen, davon 240 als Benchmark-Teilmenge. Frei zugänglich; Daten unter [miplib.zib.de](https://miplib.zib.de).*
-* ★★★ **Mittelmann, H. D.:** *Benchmarks for Optimization Software.* Arizona State University. [plato.asu.edu/bench.html](https://plato.asu.edu/bench.html) [Benchmarks] *Die laufend aktualisierte Referenzmessung für LP-, MILP- und QP-Solver. **Wichtige Einschränkung:** Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich Gurobi gegen HiGHS ist dort nicht mehr möglich.*
+* ★★★ **Mittelmann, H. D.:** *Benchmarks for Optimization Software.* Arizona State University. [plato.asu.edu/bench.html](https://plato.asu.edu/bench.html) [Benchmarks] *Die laufend aktualisierte Referenzmessung für [LP](#gloss:lineare-programmierung-lp){.glossar-link}-, MILP- und [QP](#gloss:quadratische-programmierung-qp){.glossar-link}-Solver. **Wichtige Einschränkung:** Seit 2024 sind die kommerziellen Solver auf eigenen Wunsch nicht mehr enthalten; ein direkter Vergleich [Gurobi](#gloss:gurobi){.glossar-link} gegen HiGHS ist dort nicht mehr möglich.*
* ★★ **Bolusani, S., Besançon, M., Bestuzheva, K. et al. (2024):** *The SCIP Optimization Suite 9.0.* Technischer Bericht, Zuse-Institut Berlin. arXiv:2402.17702 [Solver: SCIP] *Beschreibt Aufbau und Komponenten der Suite. Die Reihe wird fortgeschrieben — die jeweils aktuelle Fassung steht unter [scipopt.org](https://www.scipopt.org); beim Zitieren die tatsächlich benutzte Version angeben.*
-* ★★ **Perron, L. & Furnon, V.:** *OR-Tools.* Google. [developers.google.com/optimization](https://developers.google.com/optimization) · Quellcode: [github.com/google/or-tools](https://github.com/google/or-tools) [Solver: OR-Tools] *Die Sammlung, aus der das Buch CP-SAT und die Routing-Bibliothek benutzt.*
+* ★★ **Perron, L. & Furnon, V.:** *[OR-Tools](#gloss:or-tools){.glossar-link}.* Google. [developers.google.com/optimization](https://developers.google.com/optimization) · Quellcode: [github.com/google/or-tools](https://github.com/google/or-tools) [Solver: OR-Tools] *Die Sammlung, aus der das Buch [CP-SAT](#gloss:cp-sat){.glossar-link} und die [Routing-Bibliothek](#gloss:routing-bibliothek){.glossar-link} benutzt.*
## Constraint Programming und Scheduling
*Zu [Kapitel 7](#kap-cpsat).*
* ★★★ **Rossi, F., van Beek, P. & Walsh, T. (Hrsg., 2006):** *Handbook of Constraint Programming.* Foundations of Artificial Intelligence, Bd. 2. Elsevier, Amsterdam. [Constraint Programming] *Nach wie vor das Referenzwerk zu Propagation, globalen Constraints und Suchstrategien.*
-* ★★★ **Perron, L. & Furnon, V.:** *OR-Tools CP-SAT Solver Documentation.* Google. [developers.google.com/optimization/cp](https://developers.google.com/optimization/cp) [Solver: CP-SAT] *Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu `AddNoOverlap` und `AddCumulative` deckt sich mit [Kapitel 7](#kap-cpsat).*
-* ★★ **Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):** *IBM ILOG CP Optimizer for scheduling.* In: *Constraints* 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [Scheduling] *Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.*
+* ★★★ **Perron, L. & Furnon, V.:** *OR-Tools [CP](#gloss:constraint-programming-cp){.glossar-link}-[SAT](#gloss:sat-boolean-satisfiability){.glossar-link} Solver Documentation.* Google. [developers.google.com/optimization/cp](https://developers.google.com/optimization/cp) [Solver: CP-SAT] *Die einzige vollständige Beschreibung des im Buch benutzten Solvers. Der Abschnitt zu `AddNoOverlap` und `AddCumulative` deckt sich mit [Kapitel 7](#kap-cpsat).*
+* ★★ **Laborie, P., Rogerie, J., Shaw, P. & Vilím, P. (2018):** *IBM ILOG CP Optimizer for scheduling.* In: *Constraints* 23(2), S. 210–250. DOI: 10.1007/s10601-018-9281-x [[Scheduling](#gloss:scheduling){.glossar-link}] *Intervallvariablen und Ressourcenprofile aus Sicht des konkurrierenden Solvers — lehrreich für das Verständnis der Modellierungsprimitive.*
* ★★ **Biere, A., Heule, M., van Maaren, H. & Walsh, T. (Hrsg., 2021):** *Handbook of Satisfiability* (2. Aufl.). Frontiers in Artificial Intelligence and Applications, Bd. 336. IOS Press, Amsterdam. DOI: 10.3233/FAIA336 [SAT] *Die Grundlage dessen, was CP-SAT unter der Haube tut: Conflict-Driven Clause Learning, Klausellernen, Neustarts.*
## Graphen, Flüsse und Tourenplanung
@@ -29022,24 +29510,24 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 8](#kap-graphen). Neuer Abschnitt.*
* ★★★ **Ahuja, R. K., Magnanti, T. L. & Orlin, J. B. (1993):** *Network Flows: Theory, Algorithms, and Applications.* Prentice Hall, Englewood Cliffs. [Netzwerkflüsse] *Das Standardwerk zu Min-Cost-Flow, Matching und totaler Unimodularität — der Grund, warum diese Modelle schon als LP ganzzahlig lösen.*
-* ★★★ **Toth, P. & Vigo, D. (Hrsg., 2014):** *Vehicle Routing: Problems, Methods, and Applications* (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [VRP] *Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und Subtour-Eliminierung.*
+* ★★★ **Toth, P. & Vigo, D. (Hrsg., 2014):** *Vehicle Routing: Problems, Methods, and Applications* (2. Aufl.). SIAM, Philadelphia. DOI: 10.1137/1.9781611973594 [[VRP](#gloss:vehicle-routing-problem-vrp){.glossar-link}] *Das Referenzwerk zur Tourenplanung, einschließlich Kapazitäten, Zeitfenstern und [Subtour](#gloss:subtour){.glossar-link}-Eliminierung.*
* ★★ **Vidal, T., Laporte, G. & Matl, P. (2020):** *A concise guide to existing and emerging vehicle routing problem variants.* In: *European Journal of Operational Research* 286(2), S. 401–416. DOI: 10.1016/j.ejor.2019.10.010 [VRP] *Übersicht über die Variantenvielfalt — hilfreich, um die eigene Aufgabe einzuordnen, bevor man modelliert. Frei zugänglich als arXiv:1906.06750.*
-* ★ **Kuhn, H. W. (1955):** *The Hungarian method for the assignment problem.* In: *Naval Research Logistics Quarterly* 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [Zuordnungsproblem] *Das Originalverfahren hinter `scipy.optimize.linear_sum_assignment`.*
+* ★ **Kuhn, H. W. (1955):** *The Hungarian method for the assignment problem.* In: *Naval Research Logistics Quarterly* 2(1–2), S. 83–97. DOI: 10.1002/nav.3800020109 [[Zuordnungsproblem](#gloss:zuordnungsproblem){.glossar-link}] *Das Originalverfahren hinter `scipy.optimize.linear_sum_assignment`.*
## Metaheuristiken
*Zu [Kapitel 9](#kap-metaheuristiken). Neuer Abschnitt.*
-* ★★★ **Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):** *Handbook of Metaheuristics* (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 [Metaheuristiken] *Sammelband zu Tabu-Suche, Simulated Annealing, LNS, genetischen Verfahren — jeweils von den Urhebern geschrieben.*
+* ★★★ **Gendreau, M. & Potvin, J.-Y. (Hrsg., 2019):** *Handbook of Metaheuristics* (3. Aufl.). Springer, Cham. DOI: 10.1007/978-3-319-91086-4 [Metaheuristiken] *Sammelband zu [Tabu-Suche](#gloss:tabu-suche){.glossar-link}, [Simulated Annealing](#gloss:simulated-annealing){.glossar-link}, [LNS](#gloss:large-neighborhood-search-lns){.glossar-link}, genetischen Verfahren — jeweils von den Urhebern geschrieben.*
* ★★★ **Pisinger, D. & Ropke, S. (2010):** *Large Neighborhood Search.* In: Gendreau, M. & Potvin, J.-Y. (Hrsg.): *Handbook of Metaheuristics*, S. 399–419. Springer. DOI: 10.1007/978-1-4419-1665-5_13 [Metaheuristiken] *Die Darstellung des Verfahrens, das im Buch die exakte Suche auf Teilproblemen mit heuristischem Zerstören verbindet.*
* ★★ **Kirkpatrick, S., Gelatt, C. D. & Vecchi, M. P. (1983):** *Optimization by Simulated Annealing.* In: *Science* 220(4598), S. 671–680. DOI: 10.1126/science.220.4598.671 [Metaheuristiken] *Die Originalarbeit; die Abkühlungsanalogie stammt von hier.*
-* ★★ **Sörensen, K. (2015):** *Metaheuristics — the metaphor exposed.* In: *International Transactions in Operational Research* 22(1), S. 3–18. DOI: 10.1111/itor.12001 [Metaheuristiken] *Kritik an der Flut naturanaloger „neuer" Verfahren. Vor der Auswahl einer Metaheuristik zu lesen — sie erspart viel Zeit.*
+* ★★ **Sörensen, K. (2015):** *Metaheuristics — the metaphor exposed.* In: *International Transactions in Operational Research* 22(1), S. 3–18. DOI: 10.1111/itor.12001 [Metaheuristiken] *Kritik an der Flut naturanaloger „neuer" Verfahren. Vor der Auswahl einer [Metaheuristik](#gloss:metaheuristik){.glossar-link} zu lesen — sie erspart viel Zeit.*
## Spaltengenerierung und Dekomposition
*Zu [Kapitel 10](#kap-dekomposition). Neuer Abschnitt.*
-* ★★★ **Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):** *Column Generation.* Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] *Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.*
+* ★★★ **Desaulniers, G., Desrosiers, J. & Solomon, M. M. (Hrsg., 2005):** *[Column Generation](#gloss:column-generation){.glossar-link}.* Springer, New York. DOI: 10.1007/b135457 [Spaltengenerierung] *Das Referenzwerk zum Verfahren, einschließlich Stabilisierung und Branch-and-Price.*
* ★★★ **Lübbecke, M. E. & Desrosiers, J. (2005):** *Selected Topics in Column Generation.* In: *Operations Research* 53(6), S. 1007–1023. DOI: 10.1287/opre.1050.0234 [Spaltengenerierung] *Kompakter als das Buch und für den Einstieg besser geeignet; erklärt das Preisproblem sauber.*
* ★★ **Rahmaniani, R., Crainic, T. G., Gendreau, M. & Rei, W. (2017):** *The Benders decomposition algorithm: A literature review.* In: *European Journal of Operational Research* 259(3), S. 801–817. DOI: 10.1016/j.ejor.2016.12.005 [Dekomposition] *Übersicht über Varianten und Beschleunigungen — die Gegenrichtung zur Spaltengenerierung.*
@@ -29047,17 +29535,17 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 11](#kap-qp-nlp).*
-* ★★★ **Boyd, S. & Vandenberghe, L. (2004):** *Convex Optimization.* Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] *Referenzwerk für konvexe Mengen, KKT-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter [stanford.edu/~boyd/cvxbook](https://web.stanford.edu/~boyd/cvxbook/).*
-* ★★ **Kraft, D. (1988):** *A Software Package for Sequential Quadratic Programming.* DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] *Die Originalarbeit zu SLSQP — dem Verfahren hinter `scipy.optimize.minimize`.*
-* ★★ **Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):** *Applications of second-order cone programming.* In: *Linear Algebra and its Applications* 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [Konische Optimierung] *Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in [Kapitel 12](#kap-unsicherheit).*
+* ★★★ **Boyd, S. & Vandenberghe, L. (2004):** *Convex Optimization.* Cambridge University Press. DOI: 10.1017/CBO9780511804441 [Konvexe Optimierung] *Referenzwerk für konvexe Mengen, [KKT](#gloss:karush-kuhn-tucker-bedingungen-kkt){.glossar-link}-Bedingungen, QP und SDP; die theoretische Basis von CVXPY. Frei verfügbar unter [stanford.edu/~boyd/cvxbook](https://web.stanford.edu/~boyd/cvxbook/).*
+* ★★ **Kraft, D. (1988):** *A Software Package for Sequential Quadratic Programming.* DFVLR-FB 88-28, Deutsche Forschungs- und Versuchsanstalt für Luft- und Raumfahrt, Köln. [Nichtlineare Optimierung] *Die Originalarbeit zu [SLSQP](#gloss:slsqp){.glossar-link} — dem Verfahren hinter `scipy.optimize.minimize`.*
+* ★★ **Lobo, M. S., Vandenberghe, L., Boyd, S. & Lebret, H. (1998):** *Applications of second-order cone programming.* In: *Linear Algebra and its Applications* 284(1–3), S. 193–228. DOI: 10.1016/S0024-3795(98)10032-0 [[Konische Optimierung](#gloss:konische-optimierung){.glossar-link}] *Warum sich Risikoschranken und Normbedingungen als SOCP schreiben lassen — die Grundlage der Chance-Constraints-Formulierung in [Kapitel 12](#kap-unsicherheit).*
## Optimierung unter Unsicherheit: stochastisch, robust, Chance Constraints
*Zu [Kapitel 12](#kap-unsicherheit) und [Kapitel 17](#kap-supplychain). Aus dem früheren Block „Konvexe, nichtlineare und robuste Optimierung" herausgelöst und erweitert.*
-* ★★★ **Birge, J. R. & Louveaux, F. (2011):** *Introduction to Stochastic Programming* (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] *Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information (EVPI).*
-* ★★★ **Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):** *Robust Optimization.* Princeton University Press. DOI: 10.1515/9781400831050 [Robuste Optimierung] *Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.*
-* ★★★ **Bertsimas, D. & Sim, M. (2004):** *The Price of Robustness.* In: *Operations Research* 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] *Quelle des Budgeted-Uncertainty-Ansatzes: Der Parameter $\Gamma$ regelt stufenlos, wie vorsichtig die Lösung wird.*
+* ★★★ **Birge, J. R. & Louveaux, F. (2011):** *Introduction to Stochastic Programming* (2. Aufl.). Springer, New York. DOI: 10.1007/978-1-4614-0237-4 [Stochastische Optimierung] *Das Standardwerk zur zweistufigen Optimierung, Szenariobäumen und dem Wert perfekter Information ([EVPI](#gloss:evpi-expected-value-of-perfect-information){.glossar-link}).*
+* ★★★ **Ben-Tal, A., El Ghaoui, L. & Nemirovski, A. (2009):** *Robust Optimization.* Princeton University Press. DOI: 10.1515/9781400831050 [[Robuste Optimierung](#gloss:robuste-optimierung){.glossar-link}] *Die geschlossene Darstellung der robusten Gegenstücke und ihrer konischen Umformung.*
+* ★★★ **Bertsimas, D. & Sim, M. (2004):** *The Price of Robustness.* In: *Operations Research* 52(1), S. 35–53. DOI: 10.1287/opre.1030.0065 [Robuste Optimierung] *Quelle des Budgeted-Uncertainty-Ansatzes: Der [Parameter](#gloss:parameter){.glossar-link} $\Gamma$ regelt stufenlos, wie vorsichtig die Lösung wird.*
* ★★ **Nemirovski, A. & Shapiro, A. (2007):** *Convex Approximations of Chance Constrained Programs.* In: *SIAM Journal on Optimization* 17(4), S. 969–996. DOI: 10.1137/050622328 [Chance Constraints] *Zeigt, wann sich eine Wahrscheinlichkeitsbedingung konvex annähern lässt — und wann nicht.*
* ★★ **Charnes, A. & Cooper, W. W. (1959):** *Chance-Constrained Programming.* In: *Management Science* 6(1), S. 73–79. DOI: 10.1287/mnsc.6.1.73 [Chance Constraints] *Die Originalarbeit, die den Begriff geprägt hat.*
* ★★ **Savage, S. L. (2009):** *The Flaw of Averages.* Wiley, Hoboken. [Unsicherheit] *Die populäre Darstellung des Fluchs des Durchschnitts. Ohne Formeln, dafür mit den Beispielen, die in Besprechungen wirken.*
@@ -29066,8 +29554,8 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 13](#kap-dynamische-programmierung). Neuer Abschnitt.*
-* ★★★ **Powell, W. B. (2022):** *Reinforcement Learning and Stochastic Optimization: A Unified Framework for Sequential Decisions.* Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] *Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die [Kapitel 13](#kap-dynamische-programmierung) am Schluss vornimmt.*
-* ★★★ **Bertsekas, D. P. (2017):** *Dynamic Programming and Optimal Control, Vol. I* (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] *Die mathematisch strenge Darstellung des Optimalitätsprinzips und der Rückwärtsinduktion.*
+* ★★★ **Powell, W. B. (2022):** *[Reinforcement Learning](#gloss:reinforcement-learning){.glossar-link} and Stochastic Optimization: A Unified Framework for Sequential Decisions.* Wiley, Hoboken. DOI: 10.1002/9781119815068 [Dynamische Programmierung] *Ordnet dynamische Programmierung, approximative Verfahren und bestärkendes Lernen in einen gemeinsamen Rahmen — genau die Einordnung, die [Kapitel 13](#kap-dynamische-programmierung) am Schluss vornimmt.*
+* ★★★ **Bertsekas, D. P. (2017):** *Dynamic Programming and Optimal Control, Vol. I* (4. Aufl.). Athena Scientific, Belmont. [Dynamische Programmierung] *Die mathematisch strenge Darstellung des Optimalitätsprinzips und der [Rückwärtsinduktion](#gloss:rueckwaertsinduktion){.glossar-link}.*
* ★★ **Sutton, R. S. & Barto, A. G. (2018):** *Reinforcement Learning: An Introduction* (2. Aufl.). MIT Press, Cambridge. [Reinforcement Learning] *Der Standardtext zum lernenden Gegenstück. Frei verfügbar auf der Seite der Autoren.*
* ★ **Bellman, R. (1957):** *Dynamic Programming.* Princeton University Press. [Historisch] *Das Originalwerk. Für die praktische Arbeit heute durch Powell und Bertsekas ersetzt.*
@@ -29075,9 +29563,9 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 14](#kap-mehrziel). Neuer Abschnitt.*
-* ★★★ **Ehrgott, M. (2005):** *Multicriteria Optimization* (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 [Mehrkriterielle Optimierung] *Pareto-Optimalität, Skalarisierung und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.*
+* ★★★ **Ehrgott, M. (2005):** *Multicriteria Optimization* (2. Aufl.). Springer, Berlin. DOI: 10.1007/3-540-27659-9 [Mehrkriterielle Optimierung] *Pareto-Optimalität, [Skalarisierung](#gloss:skalarisierung){.glossar-link} und ihre Grenzen — insbesondere die Frage, welche Punkte der Front eine gewichtete Summe überhaupt erreichen kann.*
* ★★ **Miettinen, K. (1999):** *Nonlinear Multiobjective Optimization.* Kluwer, Boston. DOI: 10.1007/978-1-4615-5563-6 [Mehrkriterielle Optimierung] *Systematik der Verfahren, geordnet danach, wann die Präferenz des Entscheiders einfließt.*
-* ★★ **Blank, J. & Deb, K. (2020):** *pymoo: Multi-Objective Optimization in Python.* In: *IEEE Access* 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] *Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. [pymoo.org](https://pymoo.org)*
+* ★★ **Blank, J. & Deb, K. (2020):** *[pymoo](#gloss:pymoo){.glossar-link}: [Multi-Objective Optimization](#gloss:multi-objective-optimization){.glossar-link} in Python.* In: *IEEE Access* 8, S. 89497–89509. DOI: 10.1109/ACCESS.2020.2990567 [Bibliothek: pymoo] *Die im Buch für evolutionäre Pareto-Fronten benutzte Bibliothek. [pymoo.org](https://pymoo.org)*
## Lernbasierte Optimierung: maschinelles Lernen und OR
@@ -29102,22 +29590,22 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
*Zu [Kapitel 18](#kap-finanzdaten), [Kapitel 19](#kap-markowitz), [Kapitel 20](#kap-cvar) und [Kapitel 21](#kap-handelsmaschine).*
* ★★★ **Markowitz, H. (1952):** *Portfolio Selection.* In: *The Journal of Finance* 7(1), S. 77–91. DOI: 10.2307/2975974 [Portfoliotheorie] *Die Arbeit, die das Zusammenspiel von Rendite und Kovarianz zum Optimierungsproblem gemacht hat.*
-* ★★★ **Rockafellar, R. T. & Uryasev, S. (2000):** *Optimization of Conditional Value-at-Risk.* In: *Journal of Risk* 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] *Der Satz, der die CVaR-Minimierung in ein lineares Programm überführt.*
+* ★★★ **Rockafellar, R. T. & Uryasev, S. (2000):** *Optimization of Conditional Value-at-Risk.* In: *Journal of Risk* 2(3), S. 21–41. DOI: 10.21314/JOR.2000.038 [Risikomaße] *Der Satz, der die [CVaR](#gloss:conditional-value-at-risk-cvar){.glossar-link}-Minimierung in ein lineares Programm überführt.*
* ★★★ **Ledoit, O. & Wolf, M. (2004):** *A well-conditioned estimator for large-dimensional covariance matrices.* In: *Journal of Multivariate Analysis* 88(2), S. 365–411. DOI: 10.1016/S0047-259X(03)00096-4 [Kovarianzschätzung] *Quelle des von `scikit-learn` verwendeten Shrinkage-Ziels (skalierte Einheitsmatrix).*
* ★★★ **Cornuéjols, G., Peña, J. & Tütüncü, R. (2018):** *Optimization Methods in Finance* (2. Aufl.). Cambridge University Press. DOI: 10.1017/9781107297340 [Finanzoptimierung] *Die Brücke zwischen Optimierungstheorie und Finanzanwendung; deckt QP, SOCP und robuste Portfolios ab.*
* ★★★ **Almgren, R. & Chriss, N. (2001):** *Optimal Execution of Portfolio Transactions.* In: *Journal of Risk* 3(2), S. 5–39. DOI: 10.21314/jor.2001.041 [Orderausführung] *Das Modell hinter dem Beispiel zur optimalen Orderausführung in [Kapitel 13](#kap-dynamische-programmierung).*
* ★★ **Ledoit, O. & Wolf, M. (2003):** *Improved estimation of the covariance matrix of stock returns with an application to portfolio selection.* In: *Journal of Empirical Finance* 10(5), S. 603–621. DOI: 10.1016/S0927-5398(03)00007-0 [Kovarianzschätzung] *Die frühere Arbeit mit dem Konstant-Korrelations-Ziel.*
* ★★ **DeMiguel, V., Garlappi, L. & Uppal, R. (2009):** *Optimal Versus Naive Diversification: How Inefficient is the 1/N Portfolio Strategy?* In: *Review of Financial Studies* 22(5), S. 1915–1953. DOI: 10.1093/rfs/hhm075 [Portfoliotheorie] *Die Studie, die 1/N als ernstzunehmenden Vergleichsmaßstab etabliert hat — der Grund, warum im Buch jeder Optimierer gegen sie antreten muss.*
-* ★★ **López de Prado, M. (2018):** *Advances in Financial Machine Learning.* Wiley, Hoboken. [Backtesting] *Backtest-Overfitting, Deflated Sharpe Ratio, Denoising der Kovarianzmatrix. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.*
+* ★★ **López de Prado, M. (2018):** *Advances in Financial Machine Learning.* Wiley, Hoboken. [Backtesting] *[Backtest](#gloss:backtest){.glossar-link}-[Overfitting](#gloss:overfitting){.glossar-link}, Deflated [Sharpe Ratio](#gloss:sharpe-ratio){.glossar-link}, Denoising der [Kovarianzmatrix](#gloss:kovarianzmatrix){.glossar-link}. Die Literaturgrundlage der fünf Selbsttäuschungen des Backtestens.*
* ★★ **Bailey, D. H. & López de Prado, M. (2014):** *The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality.* In: *The Journal of Portfolio Management* 40(5), S. 94–107. DOI: 10.3905/jpm.2014.40.5.094 [Backtesting] *Beziffert, wie stark eine Sharpe Ratio zu korrigieren ist, wenn man viele Strategien ausprobiert hat.*
## Praxis: Betrieb, Test und Auslieferung von Optimierungsmodellen
*Zu [Kapitel 22](#kap-praxisfallen) und [Kapitel 23](#kap-testing). Neuer Abschnitt.*
-* ★★★ **Chinneck, J. W. (2008):** *Feasibility and Infeasibility in Optimization: Algorithms and Computational Methods.* Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] *Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen (IIS). Direkte Grundlage von [Anhang C](#anhang-fehlerdiagnose).*
+* ★★★ **Chinneck, J. W. (2008):** *Feasibility and [Infeasibility](#gloss:infeasibility){.glossar-link} in Optimization: Algorithms and Computational Methods.* Springer, New York. DOI: 10.1007/978-0-387-74932-7 [Fehlerdiagnose] *Das Standardwerk zur Unlösbarkeitsdiagnose, einschließlich des Deletion Filters und der irreduziblen unlösbaren Teilmengen ([IIS](#gloss:iis-irreducible-infeasible-subset){.glossar-link}). Direkte Grundlage von [Anhang C](#anhang-fehlerdiagnose).*
* ★★ **Sculley, D., Holt, G., Golovin, D. et al. (2015):** *Hidden Technical Debt in Machine Learning Systems.* In: *Advances in Neural Information Processing Systems* 28, S. 2503–2511. [Betrieb] *Über Systeme, deren Modell nur ein kleiner Teil ist — überträgt sich unmittelbar auf den Betrieb von Optimierungsdiensten. Frei zugänglich.*
-* ★★ **Kleppmann, M. (2017):** *Designing Data-Intensive Applications.* O'Reilly, Sebastopol. [Betrieb] *Snapshot-Prinzip, Zustandslosigkeit, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in [Kapitel 22](#kap-praxisfallen).*
+* ★★ **Kleppmann, M. (2017):** *Designing Data-Intensive Applications.* O'Reilly, Sebastopol. [Betrieb] *[Snapshot-Prinzip](#gloss:snapshot-prinzip){.glossar-link}, [Zustandslosigkeit](#gloss:zustandslosigkeit){.glossar-link}, Versionierung und Reproduzierbarkeit — die Architekturfragen hinter der OR-Plattform in [Kapitel 22](#kap-praxisfallen).*
* ★★ **Ramírez, S.:** *FastAPI Documentation.* [fastapi.tiangolo.com](https://fastapi.tiangolo.com) [Betrieb] *Die Grundlage des Optimierungsdienstes in [Kapitel 23](#kap-testing), einschließlich der Eingabevalidierung über `pydantic`.*
## Software, Dokumentation und Datenquellen
@@ -29129,7 +29617,7 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
* **Pyomo:** [pyomo.org](https://www.pyomo.org)
* **linopy:** [linopy.readthedocs.io](https://linopy.readthedocs.io)
* **pymoo:** [pymoo.org](https://pymoo.org)
-* **scikit-learn, Covariance Estimation:** [scikit-learn.org/stable/modules/covariance.html](https://scikit-learn.org/stable/modules/covariance.html)
+* **[scikit-learn](#gloss:scikit-learn){.glossar-link}, Covariance Estimation:** [scikit-learn.org/stable/modules/covariance.html](https://scikit-learn.org/stable/modules/covariance.html)
* **MIPLIB 2017 — Instanzensammlung:** [miplib.zib.de](https://miplib.zib.de)
* **Mittelmann-Benchmarks:** [plato.asu.edu/bench.html](https://plato.asu.edu/bench.html)
@@ -29138,7 +29626,7 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
* **INFORMS** — Institute for Operations Research and the Management Sciences. [www.informs.org](https://www.informs.org)
* **GOR** — Gesellschaft für Operations Research e. V., deutschsprachiger Fachverband. [www.gor-ev.de](https://www.gor-ev.de)
* **EURO** — Association of European Operational Research Societies. [www.euro-online.org](https://www.euro-online.org)
-* **Basel Committee on Banking Supervision:** *Minimum capital requirements for market risk* (Basel III, FRTB). Bank for International Settlements. *Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf Expected Shortfall.*
+* **Basel Committee on Banking Supervision:** *Minimum capital requirements for market risk* (Basel III, FRTB). Bank for International Settlements. *Grundlage der aufsichtsrechtlichen Umstellung von Value at Risk auf [Expected Shortfall](#gloss:expected-shortfall){.glossar-link}.*
* **Europäische Union:** *MiFID II* (Richtlinie 2014/65/EU) sowie die ESMA-Leitlinien zum algorithmischen Handel. *Regulatorischer Rahmen für die Handelsmaschine aus [Kapitel 21](#kap-handelsmaschine).*
## Änderungsprotokoll
@@ -29151,7 +29639,7 @@ Frei zugängliche Fassungen sind vermerkt. Wo eine DOI angegeben ist, führt sie
**Zur Vorgabe „bevorzugt Werke ab 2018":** Sie ließ sich nur teilweise einlösen, und zwar aus der Sache heraus. In weiten Teilen des Operations Research sind die maßgeblichen Werke älter — Ahuja/Magnanti/Orlin (1993) zu Netzwerkflüssen, Boyd/Vandenberghe (2004) zur konvexen Optimierung, Ehrgott (2005) zur Mehrkriterialität, Birge/Louveaux (2011) zur stochastischen Programmierung. Sie durch jüngere, aber weniger maßgebliche Titel zu ersetzen hätte die Liste verschlechtert. Neuer als 2018 ist konsequent dort, wo sich das Feld tatsächlich bewegt hat: Solver-Benchmarks, Python-Bibliotheken und lernbasierte Optimierung.
-**Neue Themenabschnitte: zwölf.** Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: *Modellierung in Python*, *Lineare und ganzzahlige Optimierung*, *Moderne Solver und Benchmarks*, *Graphen, Flüsse und Tourenplanung*, *Metaheuristiken*, *Spaltengenerierung und Dekomposition*, *Optimierung unter Unsicherheit*, *Dynamische Programmierung*, *Mehrkriterielle Optimierung*, *Lernbasierte Optimierung*, *Supply Chain, Energie und Produktionsplanung* sowie *Praxis: Betrieb, Test und Auslieferung*. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung" wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
+**Neue Themenabschnitte: zwölf.** Sie entsprechen Kapiteln, die in der Vorgängerfassung keinen eigenen Literaturblock hatten: *Modellierung in Python*, *Lineare und ganzzahlige Optimierung*, *Moderne Solver und Benchmarks*, *[Graphen](#gloss:graphen){.glossar-link}, Flüsse und Tourenplanung*, *Metaheuristiken*, *Spaltengenerierung und Dekomposition*, *Optimierung unter Unsicherheit*, *Dynamische Programmierung*, *Mehrkriterielle Optimierung*, *Lernbasierte Optimierung*, *Supply Chain, Energie und Produktionsplanung* sowie *Praxis: Betrieb, Test und Auslieferung*. Der frühere Block „Konvexe, nichtlineare und robuste Optimierung" wurde dabei geteilt, weil das Buch Unsicherheit in einem eigenen Kapitel behandelt.
**Nicht angelegt wurden zwei erwogene Abschnitte:** *Diskrete Ereignissimulation* und *Cloud-basierte Solver-Nutzung*. Beide Themen kommen im Buch nicht vor — eine Literaturliste soll das Buch erschließen und nicht darüber hinausweisen. Der API-gestützte Betrieb eines Solvers ist stattdessen im Praxisblock erfasst.
diff --git a/Operations_Research_mit_Python_Version_04.pdf b/Operations_Research_mit_Python_Version_04.pdf
index 097e6bd..44c6049 100644
Binary files a/Operations_Research_mit_Python_Version_04.pdf and b/Operations_Research_mit_Python_Version_04.pdf differ
diff --git a/Operations_Research_mit_Python_Version_04/94_Anhang_Glossar.md b/Operations_Research_mit_Python_Version_04/94_Anhang_Glossar.md
index 5020862..75bb473 100644
--- a/Operations_Research_mit_Python_Version_04/94_Anhang_Glossar.md
+++ b/Operations_Research_mit_Python_Version_04/94_Anhang_Glossar.md
@@ -7,1143 +7,1644 @@ dorthin führt in der Online-Fassung direkt zur Erklärung im Zusammenhang. Wer
## A
+::: {#gloss:absolutbetrag}
**Absolutbetrag**{idx:Absolutbetrag (Modellierungsmuster)} — Modellierungsmuster für $|x-z|$:
Der Betrag selbst ist nicht linear, lässt sich aber durch eine Hilfsvariable $d$ mit den beiden
Bedingungen $x-z \le d$ und $z-x \le d$ ersetzen. Das funktioniert nur, wenn $d$ minimiert wird
— sonst bleibt die Schranke unausgeschöpft. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:adjusted-close}
**Adjusted Close**{idx:Adjusted Close} — Um Dividenden und Splits bereinigter Schlusskurs. Nur
mit ihm sind Renditen über längere Zeiträume vergleichbar; der unbereinigte Schlusskurs erzeugt
an jedem Ausschüttungstag einen Scheinverlust. →
{ref:sec:finanzdaten-diskrete-und-logarithmische-renditen}
+:::
+::: {#gloss:aktivierungsschalter}
**Aktivierungsschalter**{idx:Aktivierungsschalter (Modellierungsmuster)} — Modellierungsmuster,
das eine kontinuierliche Größe an eine Binärvariable koppelt: $x \le M\,y$ erlaubt $x > 0$ nur,
wenn $y = 1$ ist. Grundbaustein für Fixkosten, Standortentscheidungen und Mindestmengen. →
{ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:almgren-chriss-modell}
**Almgren-Chriss-Modell**{idx:Almgren-Chriss-Modell} — Standardmodell der optimalen
Orderausführung. Es löst den Zielkonflikt zwischen Marktauswirkung bei schnellem Handeln und
Volatilitätsrisiko bei langsamem Handeln und liefert einen Ausführungsplan über mehrere
Perioden. →
{ref:sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung}
+:::
+::: {#gloss:alternativoptima}
**Alternativoptima**{idx:Alternativoptima} — Mehrere Lösungen mit identischem Zielfunktionswert.
Sie treten auf, wenn die Zielfunktion parallel zu einer Kante des Polyeders verläuft oder die
Matrix des quadratischen Terms nur semidefinit ist. Der Solver meldet dann eine beliebige davon
— zwei Läufe können unterschiedlich aussehen und trotzdem beide optimal sein. →
{ref:sec:qp-nlp-das-quadratische-programm-qp}
+:::
+::: {#gloss:approximate-dynamic-programming}
**Approximate Dynamic Programming**{idx:Approximate Dynamic Programming} — Sammelbegriff für
Verfahren, die die Wertfunktion der dynamischen Programmierung nicht exakt tabellieren, sondern
annähern — etwa durch Regression oder ein neuronales Netz. Antwort auf den Fluch der
Dimensionalität, wenn der Zustandsraum zu groß zum Durchzählen wird. →
{ref:sec:praxisfallen-weiterfuehrende-literatur-und-roadmap}
+:::
## B
+::: {#gloss:backtest}
+**Backtest**{idx:Backtest} — Rückblickender Test einer Strategie auf historischen Daten. Ein
+Backtest ist nur so glaubwürdig wie seine Daten, seine Kostenannahme und seine Trennung von
+Trainings- und Testzeitraum — {ref:kap:handelsmaschine} zeigt die fünf Selbsttäuschungen, die
+ihn wertlos machen. → {ref:kap:handelsmaschine}
+:::
+
+::: {#gloss:bedingte-kopplung}
**Bedingte Kopplung**{idx:Bedingte Kopplung (Konjunktion)} — Modellierungsmuster für „wenn A
**und** B, dann C“. Umgesetzt über $y_C \ge y_A + y_B - 1$: Nur wenn beide Voraussetzungen
gesetzt sind, wird die Folge erzwungen. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:bellman-gleichung}
**Bellman-Gleichung**{idx:Bellman-Gleichung} — Rekursionsgleichung der dynamischen
Programmierung: Der Wert eines Zustands ist die Summe aus den unmittelbaren Kosten der besten
Aktion und dem Wert des Folgezustands. Sie zerlegt eine mehrstufige Entscheidung in eine Kette
einstufiger. → {ref:sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip}
+:::
+::: {#gloss:benders-zerlegung}
**Benders-Zerlegung**{idx:Benders-Zerlegung} — Dekompositionsverfahren für Probleme mit wenigen
„schweren“ und vielen „leichten“ Variablen. Ein Masterproblem entscheidet über die schweren, ein
Subproblem über den Rest und schickt Schnitte (Benders-Cuts) zurück, bis beide übereinstimmen. →
{ref:sec:praxisfallen-weiterfuehrende-literatur-und-roadmap}
+:::
+::: {#gloss:big-m-methode}
**Big-M-Methode**{idx:Big-M-Methode} — Modellierungstrick, der logische Bedingungen über eine
hinreichend große Konstante $M$ an eine Binärvariable koppelt. $M$ sollte **so klein wie
möglich** gewählt werden: Große Werte weichen die Relaxation auf, verschlechtern die Laufzeit
und können zu Trickle Flow führen. →
{ref:sec:milp-modellierungstricks-big-m-und-logische-bedingungen}
+:::
+::: {#gloss:binaervariable}
**Binärvariable**{idx:Binärvariable} — Entscheidungsvariable mit Wertebereich $\{0,1\}$. Sie
schaltet Fixkosten, Kapazitäten oder logische Alternativen an und aus und macht aus einem
linearen Programm ein gemischt-ganzzahliges. →
{ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:bipartites-matching}
**Bipartites Matching**{idx:Bipartites Matching} — Zuordnung zwischen zwei disjunkten Mengen —
etwa Personen und Aufgaben — mit maximalem Nutzen oder minimalen Kosten. Klassisch gelöst durch
den Ungarischen Algorithmus; die Modellmatrix ist total unimodular, weshalb schon das LP
ganzzahlige Ecken hat. → {ref:sec:graphen-bipartites-matching-das-zuordnungsproblem}
+:::
+::: {#gloss:black-box-effekt}
**Black-Box-Effekt**{idx:Black-Box-Effekt} — Ablehnung eines Optimierungssystems, weil seine
Entscheidungen nicht nachvollziehbar sind. Gegenmittel sind ein Constraint-Trace für
Einzelentscheidungen und eine Kostenzerlegung für Bewertungen. Akzeptanz ist keine Nebensache,
sondern Betriebsvoraussetzung. → {ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:branch-and-bound}
**Branch-and-Bound**{idx:Branch-and-Bound} — Exaktes Verfahren für ganzzahlige Probleme: Der
Suchraum wird rekursiv zerlegt (Branching), und ein Zweig wird verworfen, sobald seine
Relaxation schlechter ist als die beste bekannte Lösung (Bounding). Ohne das Abschneiden wäre es
bloßes Durchprobieren. → {ref:sec:milp-branch-and-bound}
+:::
+::: {#gloss:branch-and-cut}
**Branch-and-Cut**{idx:Branch-and-Cut} — Kombination aus Branch-and-Bound und
Schnittebenenverfahren: In den Knoten des Suchbaums werden zusätzlich gültige Ungleichungen
erzeugt, die gebrochene Lösungen abschneiden. Der Standardalgorithmus heutiger MILP-Solver. →
{ref:sec:oekosystem-die-zwei-schichten-architektur}
+:::
+::: {#gloss:brute-force}
**Brute Force**{idx:Brute Force} — Vollständiges Durchprobieren aller Möglichkeiten. Für kleine
Instanzen ein nützlicher Kontrollwert, in der Praxis meist chancenlos: Schon 20 binäre
Entscheidungen ergeben über eine Million Kombinationen. →
{ref:sec:einfuehrung-was-ist-operations-research-wirklich}
+:::
+::: {#gloss:budgeted-uncertainty}
**Budgeted Uncertainty**{idx:Budgeted Uncertainty} — Robustheitsansatz nach Bertsimas und Sim:
Höchstens $\Gamma$ von $n$ Parametern nehmen gleichzeitig ihren ungünstigsten Wert an. Der
Parameter $\Gamma$ regelt stufenlos, wie vorsichtig die Lösung wird — bei $\Gamma = 0$ nominal,
bei $\Gamma = n$ maximal pessimistisch. →
{ref:sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
+:::
+::: {#gloss:budgetlimit}
**Budgetlimit**{idx:Budgetlimit} — Nebenbedingung, die die Summe gewichteter Entscheidungen nach
oben begrenzt. Der zugehörige Schattenpreis beantwortet unmittelbar, was eine zusätzliche
Budgeteinheit einbrächte. → {ref:anhang:modellierungsmuster}
+:::
## C
+::: {#gloss:calmar-ratio}
**Calmar Ratio**{idx:Calmar Ratio} — Jahresrendite geteilt durch den Betrag des maximalen
Drawdowns. Ergänzt die Sharpe Ratio um die Verlustperspektive: Sie misst Ertrag je Einheit des
schlimmsten erlebten Rückschlags statt je Einheit Schwankung. →
{ref:sec:handelsmaschine-die-engine}
+:::
+::: {#gloss:cdcl}
**CDCL**{idx:CDCL} — *Conflict-Driven Clause Learning* — das Suchprinzip moderner SAT-Solver.
Aus jedem Widerspruch wird eine Sperrklausel abgeleitet, die verhindert, dass dieselbe Sackgasse
erneut betreten wird; anschließend springt die Suche gezielt zurück statt Schritt für Schritt. →
{ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:chance-constraint}
**Chance Constraint**{idx:Chance Constraint} — Nebenbedingung, die nicht immer, sondern nur mit
vorgegebener Wahrscheinlichkeit gelten muss — etwa „die Versorgung reicht in 95 % der Fälle“.
Bei normalverteilter Unsicherheit wird daraus eine konische Bedingung, sonst hilft eine
szenariobasierte Big-M-Formulierung. → {ref:sec:unsicherheit-chance-constraints}
+:::
+::: {#gloss:column-generation}
**Column Generation**{idx:Column Generation} — Verfahren für Modelle mit sehr vielen Variablen:
Gerechnet wird auf einer kleinen Teilmenge, und ein Preisproblem sucht wiederholt die eine
Spalte, die sich noch lohnt. Erst wenn keine mehr existiert, ist das Optimum bewiesen. →
{ref:sec:metaheuristiken-abgrenzung}
+:::
+::: {#gloss:conditional-value-at-risk-cvar}
**Conditional Value at Risk (CVaR)**{idx:Conditional Value at Risk (CVaR)} — Erwarteter Verlust
in den schlechtesten Szenarien jenseits des Value at Risk. Anders als der VaR ist der CVaR
kohärent und subadditiv und lässt sich nach Rockafellar und Uryasev exakt als lineares Programm
formulieren. → {ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:conflict-learning}
**Conflict Learning**{idx:CDCL!Conflict Learning} — Technik moderner SAT- und CP-Solver, aus
jedem erkannten Widerspruch eine Sperrklausel abzuleiten. Der Solver lernt so aus Fehlschlägen,
statt gleichartige Sackgassen immer wieder zu durchsuchen. →
{ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:constraint-attribution}
**Constraint Attribution**{idx:Constraint Attribution} — Diagnoseverfahren für den häufigsten
Praxisfall: Das Modell rechnet, der Plan ist zulässig — und trotzdem enttäuschend. Jede
Bedingungsgruppe wird einzeln gelockert und gemessen, wie sich der Zielwert ändert. Dasselbe
Vorgehen wie der Deletion Filter, nur mit Kosten statt Zulässigkeit als Kriterium. →
{ref:sec:praxisfallen-attribution}
+:::
+::: {#gloss:constraint-programming-cp}
**Constraint Programming (CP)**{idx:Constraint Programming (CP)} — Paradigma, das nicht über
Zielfunktionsgradienten, sondern über logische Verträglichkeit sucht: Constraints schränken
Wertebereiche ein, bis eine zulässige Belegung gefunden ist. Stark bei Reihenfolgen,
Schichtplänen und Ressourcenkonflikten. → {ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:constraint-propagation}
**Constraint Propagation**{idx:Constraint Propagation} — Kernmechanismus von CP-Solvern: Aus
einer Zuweisung werden unmögliche Werte anderer Variablen sofort entfernt. Der Suchbaum
schrumpft dadurch, bevor er überhaupt durchsucht wird. Genau hier liegt der Vorteil globaler
Constraints — ihre Propagatoren schließen weit mehr Werte aus als die gleichwertige Menge
einzelner Ungleichungen. → {ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:constraint-trace}
**Constraint-Trace**{idx:Constraint-Trace} — Nachvollziehbare Begründung einer einzelnen
Solver-Entscheidung — „Warum bekommt Person A die Stunde und nicht Person B?“ — anhand der
bindenden Bedingungen und Strafpunkte. Das Gegenmittel zum Black-Box-Effekt. →
{ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:cp-sat}
**CP-SAT**{idx:CP-SAT} — Constraint-Programming-Solver von OR-Tools. Er übersetzt ein Modell in
boolesche Erfüllbarkeit und löst es mit Propagation, Conflict Learning und paralleler Suche. Für
Reihenfolge- und Zuordnungsprobleme meist schneller als ein MILP-Solver. →
{ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:cp-sat-solver}
**CP-SAT-Solver**{idx:CP-SAT-Solver} — Die Solver-Komponente von OR-Tools, angesprochen über
`cp_model.CpSolver()`. Sie liefert neben der Lösung Statuscode, Laufzeit und die beste bekannte
Schranke und lässt sich über `num_workers` auf mehrere Kerne verteilen. →
{ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:csr-format}
**CSR-Format**{idx:CSR-Format} — *Compressed Sparse Row* — Speicherformat für dünn besetzte
Matrizen, das nur die von null verschiedenen Einträge samt ihrer Positionen ablegt. Für
Optimierungsmodelle mit zehntausenden Nebenbedingungen der Unterschied zwischen passt und passt
nicht in den Speicher. → {ref:sec:oekosystem-ein-system-vier-programmieransaetze}
+:::
+::: {#gloss:cvrptw}
**CVRPTW**{idx:CVRPTW} — *Capacitated Vehicle Routing Problem with Time Windows* — Tourenplanung
mit einer Flotte, Kapazitätsgrenzen je Fahrzeug und Zeitfenstern je Kunde. Die praxisnahe
Variante des VRP und Standardfall der Routing-Bibliothek von OR-Tools. →
{ref:sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
+:::
+::: {#gloss:cvxpy}
**CVXPY**{idx:CVXPY} — Modellierungssprache für konvexe Optimierung. Sie schreibt das Problem in
mathematiknaher Notation, prüft die Konvexität nach den Regeln der Disciplined Convex
Programming und reicht es an einen passenden Solver weiter. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
## D
+::: {#gloss:data-snooping}
**Data Snooping**{idx:Data Snooping} — Wiederholtes Ausprobieren von Strategien auf denselben
Daten, bis eine gut aussieht. Der Fund ist dann eine Eigenschaft der Stichprobe, nicht des
Marktes — gefährlicher als ein Programmierfehler, weil das Ergebnis plausibel wirkt. →
{ref:sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens}
+:::
+::: {#gloss:dcp-disciplined-convex-programming}
+**DCP (Disciplined Convex Programming)**{idx:DCP} — Regelwerk von CVXPY, das jede Variable als
+konkav, konvex oder affin ausweist und aus der Kombination der Bausteine die
+Konkavität/Konvexität der Zielfunktion und der Nebenbedingungen erschließt. {ref:kap:qp-nlp}
+erklärt es; ein `DCPError` meldet eine Verletzung. → {ref:kap:qp-nlp}
+:::
+
+::: {#gloss:dcperror}
**DCPError**{idx:DCPError} — Fehlermeldung von CVXPY, wenn ein Ausdruck die Regeln der
*Disciplined Convex Programming* verletzt. Sie besagt nicht, dass das Problem unlösbar ist,
sondern dass CVXPY die Konvexität nicht nachweisen kann — meist hilft eine äquivalente
Umformung. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:deletion-filter}
**Deletion Filter**{idx:Deletion Filter} — Verfahren zur Eingrenzung von Unlösbarkeit. Es fragt
nicht „ist *diese* Bedingung schuld?“, sondern „wird *diese* Bedingung für den Widerspruch
überhaupt gebraucht?“, und findet dadurch auch dann eine minimale unlösbare Teilmenge, wenn sich
mehrere Widersprüche überlagern. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:diskrete-rendite}
**Diskrete Rendite**{idx:Diskrete Rendite} — Einfache Rendite $(P_t - P_{t-1})/P_{t-1}$. Sie ist
**über Titel** additiv und deshalb die richtige Wahl für Portfoliogewichte — anders als die
logarithmische Rendite, die über die Zeit additiv ist. →
{ref:sec:finanzdaten-diskrete-und-logarithmische-renditen}
+:::
+::: {#gloss:diversifikation}
**Diversifikation**{idx:Diversifikation} — Risikoreduktion durch Verteilung auf Titel, deren
Renditen nicht vollständig gleichlaufen. Der Effekt steckt vollständig in den Kovarianzen: Bei
Korrelation 1 verschwindet er, bei negativer Korrelation ist er am größten. →
{ref:sec:markowitz-warum-diversifikation-funktioniert}
+:::
+::: {#gloss:dto-data-transfer-object}
**DTO (Data Transfer Object)** — Schlichtes Objekt, das nur Daten transportiert und keine Logik
enthält. Im Buch trägt die `Loesung`-Klasse in `or_kern.py` das Ergebnis eines Solverlaufs so
aus der Rechenschicht heraus. → {ref:sec:oekosystem-ein-system-vier-programmieransaetze}
+:::
+::: {#gloss:dual-simplex}
**Dual Simplex**{idx:Dual Simplex} — Variante des Simplex-Verfahrens, die eine dual zulässige
Basis beibehält und die primale Zulässigkeit herstellt. Das Verfahren der Wahl beim Warm-Start,
etwa in jedem Knoten eines Branch-and-Bound-Baums. →
{ref:sec:oekosystem-die-zwei-schichten-architektur}
+:::
+::: {#gloss:duales-problem}
**Duales Problem**{idx:Duales Problem} — Das jedem Optimierungsproblem zugeordnete Gegenstück,
dessen Variablen die Nebenbedingungen des Originals bepreisen. Seine Optimalwerte sind die
Schattenpreise; im Optimum stimmen beide Zielfunktionswerte überein. →
{ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:dualitaetstheorie}
**Dualitätstheorie**{idx:Dualitätstheorie} — Theorie des Zusammenhangs zwischen einem
Optimierungsproblem und seinem Dualproblem. Der schwache Dualitätssatz liefert stets eine
Schranke, der starke besagt, dass bei linearen Programmen beide Optima zusammenfallen. →
{ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:dynamische-programmierung-dp}
**Dynamische Programmierung (DP)**{idx:Dynamische Programmierung} — Lösungsprinzip für
mehrstufige Entscheidungen: Das Problem wird in Zustände und Stufen zerlegt und rückwärts
gelöst, wobei jeder Zustand nur einmal bewertet wird. Voraussetzung ist das Optimalitätsprinzip.
→ {ref:sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip}
+:::
## E
+::: {#gloss:ecke-extrempunkt}
**Ecke (Extrempunkt)**{idx:Ecke (Extrempunkt)} — Punkt des zulässigen Bereichs, der sich nicht
als echte Mischung zweier anderer zulässiger Punkte schreiben lässt. Nach dem Fundamentalsatz
der linearen Optimierung liegt in einer Ecke stets ein Optimum — deshalb sucht der Simplex nur
dort. → {ref:sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder}
+:::
+::: {#gloss:effizienzgrenze-efficient-frontier}
**Effizienzgrenze (Efficient Frontier)**{idx:Effizienzgrenze} — Kurve aller Portfolios, die zu
gegebenem Risiko die höchste erwartete Rendite liefern. Portfolios darunter sind dominiert,
Portfolios darüber unerreichbar. → {ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:entartung}
**Entartung**{idx:Entartung} — Zustand, in dem mehr Nebenbedingungen aktiv sind, als Variablen
frei bleiben. Der Simplex kann dann Basiswechsel ohne Zielwertverbesserung durchführen, und die
Schattenpreise sind nicht mehr eindeutig — zwei Solver dürfen verschiedene, gleichermaßen
korrekte Dualwerte melden. → {ref:sec:lp-entartung}
+:::
+::: {#gloss:entscheidungsvariable}
**Entscheidungsvariable**{idx:Entscheidungsvariable} — Die vom Solver frei wählbare Größe eines
Modells. Ihr Wertebereich — kontinuierlich, ganzzahlig oder binär — bestimmt die Problemklasse
und damit den Lösungsaufwand. → {ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:entscheidungsvektor}
**Entscheidungsvektor**{idx:Entscheidungsvektor} — Zusammenfassung aller Entscheidungsvariablen
zu einem Vektor $\mathbf{x}$. Erst diese Schreibweise macht die kompakte Matrixform eines
Modells möglich. → {ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:entweder-oder-bedingung}
**Entweder-Oder-Bedingung**{idx:Entweder-Oder-Bedingung} — Modellierungsmuster für sich
ausschließende Alternativen: Genau eine von mehreren Bedingungen soll gelten. Umgesetzt über
Binärvariablen mit Summe 1 und Big-M-Kopplung an die jeweilige Bedingung. →
{ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:error-maximizer-effekt}
**Error-Maximizer-Effekt**{idx:Error-Maximizer-Effekt} — Eigenschaft der
Mean-Variance-Optimierung, Schätzfehler zu verstärken statt auszugleichen. Der Optimierer sucht
gezielt die Richtungen mit der niedrigsten geschätzten Varianz — und das sind genau jene, deren
Varianz am stärksten unterschätzt wurde. → {ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:evpi-expected-value-of-perfect-information}
**EVPI (Expected Value of Perfect Information)**{idx:EVPI} — Differenz zwischen den erwarteten
Kosten unter Unsicherheit und den Kosten bei perfektem Wissen. Obergrenze für den Wert jeder
Prognoseverbesserung: Keine noch so gute Vorhersage kann mehr einbringen. →
{ref:sec:unsicherheit-zweistufige-stochastische-programmierung}
+:::
+::: {#gloss:exklusiv-oder}
**Exklusiv-Oder**{idx:Exklusiv-Oder} — Modellierungsmuster für „genau eine von zwei Optionen“:
$y_A + y_B = 1$. Die Abgrenzung zum einfachen Oder ($y_A + y_B \ge 1$) ist in der Praxis eine
häufige Fehlerquelle. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:expected-shortfall}
**Expected Shortfall**{idx:Expected Shortfall} — Anderer Name für den Conditional Value at Risk;
in der Bankenregulierung die gebräuchliche Bezeichnung. Beide meinen den erwarteten Verlust
jenseits des VaR-Quantils. → {ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:explainable-or}
**Explainable OR**{idx:Explainable OR} — Nachvollziehbarmachung von Solver-Ergebnissen über
Schattenpreise, aktive Restriktionen, Constraint-Trace und Kostenzerlegung. Das Gegenstück zu
Explainable AI für Optimierungsmodelle. →
{ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
## F
+::: {#gloss:fallback-strategie}
**Fallback-Strategie**{idx:Fallback-Strategie} — Festgelegtes Vorgehen für den Fall, dass der
Solver kein Ergebnis liefert: letzter gültiger Plan, regelbasierte Notlösung, Alarm. Niemals:
nichts. → {ref:sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
+:::
+::: {#gloss:falsche-dualwerte}
**Falsche Dualwerte**{idx:Falsche Dualwerte (Fehlerbild)} — Fehlerbild, bei dem Schattenpreise
unplausibel wirken. Zwei Ursachen dominieren: Entartung macht sie mehrdeutig, und ganzzahlige
Modelle haben streng genommen gar keine Dualwerte — was ein MILP-Solver zurückgibt, gehört zur
Relaxation des letzten Knotens. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:fat-tails}
**Fat Tails**{idx:Fat Tails} — Verteilungsränder, die dicker auslaufen als bei der
Normalverteilung. Extremereignisse sind dann deutlich häufiger als das Normalmodell vorhersagt —
der Grund, warum varianzbasierte Risikomaße das Verlustrisiko unterschätzen. →
{ref:sec:cvar-die-zwei-schwaechen-des-markowitz-modells}
+:::
+::: {#gloss:fluch-der-dimensionalitaet}
**Fluch der Dimensionalität**{idx:Fluch der Dimensionalität} — Exponentielles Wachstum des
Zustandsraums mit jeder zusätzlichen Zustandsdimension. Es begrenzt die dynamische
Programmierung und motiviert näherungsweise Verfahren. →
{ref:sec:dynamische-programmierung-der-fluch-der-dimensionalitaet}
+:::
+::: {#gloss:fluch-des-durchschnitts-flaw-of-averages}
**Fluch des Durchschnitts (Flaw of Averages)**{idx:Fluch des Durchschnitts} — Systematischer
Fehler beim Planen mit Erwartungswerten statt mit Verteilungen. Das Ergebnis der
Durchschnittsrechnung ist im Allgemeinen nicht der Durchschnitt der Ergebnisse; formal folgt das
aus der Jensenschen Ungleichung. → {ref:sec:unsicherheit-der-fluch-des-durchschnitts}
+:::
+::: {#gloss:flusserhaltung}
**Flusserhaltung**{idx:Flusserhaltung} — Bedingung, dass an jedem Knoten Abfluss minus Zufluss
dem Saldo des Knotens entspricht — das Kirchhoff-Gesetz der Netzwerkoptimierung. Summieren sich
die Salden nicht zu null, ist das Problem grundsätzlich unlösbar. →
{ref:sec:graphen-graphen-als-modellsprache}
+:::
+::: {#gloss:fundamentalsatz-der-linearen-optimierung}
**Fundamentalsatz der linearen Optimierung**{idx:Fundamentalsatz der linearen Optimierung} — Das
Optimum eines lösbaren linearen Programms wird stets in mindestens einer Ecke des zulässigen
Polyeders angenommen. Die Rechtfertigung dafür, nur endlich viele Ecken statt unendlich vieler
Punkte zu prüfen. → {ref:sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder}
+:::
## G
+::: {#gloss:gemischt-ganzzahlige-optimierung-milp}
**Gemischt-ganzzahlige Optimierung (MILP)**{idx:MILP} — *Mixed-Integer Linear Programming* —
lineares Modell mit mindestens einer ganzzahligen oder binären Variablen. Die Ganzzahligkeit
macht das Problem NP-schwer; gelöst wird es mit Branch-and-Cut. →
{ref:sec:milp-warum-runden-fundamental-scheitert}
+:::
+::: {#gloss:genetischer-algorithmus}
**Genetischer Algorithmus**{idx:Genetischer Algorithmus} — Metaheuristik, die eine Population
von Lösungen durch Selektion, Rekombination und Mutation weiterentwickelt. Sie gibt keine
Optimalitätsgarantie, kommt dafür mit Zielfunktionen zurecht, die weder linear noch
differenzierbar sind. → {ref:sec:metaheuristiken-abgrenzung}
+:::
+::: {#gloss:gestaffelte-preise}
**Gestaffelte Preise**{idx:Gestaffelte Preise (stückweise linear)} — Modellierungsmuster für
Mengenrabatte und Tarifstufen. Die Menge wird in Segmente zerlegt, jedes mit eigenem Preis;
Binärvariablen erzwingen, dass ein Segment erst genutzt wird, wenn das vorhergehende
ausgeschöpft ist. Ohne diese Reihenfolgebedingung sucht sich der Solver einfach das billigste
Segment heraus. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:gil-global-interpreter-lock}
**GIL (Global Interpreter Lock)** — Sperre des CPython-Interpreters, die verhindert, dass zwei
Threads gleichzeitig Python-Code ausführen. Sie ist der Grund, warum echte Parallelität in
Python über Prozesse statt Threads läuft — Solver umgehen sie, weil sie in C++ rechnen. →
[Notation und Abkürzungen](01_Notation_und_Abkuerzungen.md)
+:::
+::: {#gloss:gini-koeffizient}
**Gini-Koeffizient**{idx:Gini-Koeffizient} — Maß für die Ungleichverteilung einer Größe zwischen
0 (alle gleich) und 1 (einer bekommt alles). Im Buch als Fairnesskennzahl für
Wunscherfüllungsquoten verwendet. → [Projektwerkstatt](59_Projektwerkstatt.md)
+:::
+::: {#gloss:gleitendes-fenster}
**Gleitendes Fenster**{idx:Gleitendes Fenster} — Auswertung, bei der ein Zeitfenster fester
Länge über die Daten geschoben wird. Grundlage rollierender Schätzungen und des
Walk-Forward-Backtests. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:global-minimum-variance-portfolio-gmv}
**Global Minimum Variance Portfolio (GMV)**{idx:GMV (Global Minimum Variance)} — Portfolio
kleinstmöglicher Varianz. Es benötigt **keine Renditeprognose** und ist deshalb deutlich
robuster gegen Schätzfehler als das renditemaximierende Mean-Variance-Portfolio. →
{ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:globale-constraints}
**Globale Constraints**{idx:Globale Constraints} — Vorgefertigte Bausteine wie `AllDifferent`,
`NoOverlap` oder `Cumulative`, die häufige Strukturen kompakt ausdrücken. Ihr eigentlicher Wert
sind die spezialisierten Propagatoren, die weit mehr Werte ausschließen als die gleichwertige
Menge einzelner Ungleichungen. → {ref:sec:cpsat-globale-constraints-die-bausteine-von-cp}
+:::
+::: {#gloss:globales-minimum}
**Globales Minimum**{idx:Globales Minimum} — Der kleinste Zielfunktionswert im gesamten
zulässigen Bereich. Bei konvexen Problemen ist jedes lokale Minimum zugleich global — der Grund,
warum Konvexität die Grenze zwischen leicht und schwer markiert. →
{ref:sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und}
+:::
+::: {#gloss:glop}
**GLOP** — *Google Linear Optimization Package* — der LP-Solver in OR-Tools. Er löst rein
kontinuierliche Probleme; sobald Ganzzahligkeit ins Spiel kommt, übernimmt CP-SAT oder ein
MILP-Backend. → [Notation und Abkürzungen](01_Notation_und_Abkuerzungen.md)
+:::
+::: {#gloss:goldene-regel-des-backtestens}
**Goldene Regel des Backtestens**{idx:Goldene Regel des Backtestens} — Zum Zeitpunkt $t$ darf
nur Information verwendet werden, die zu $t$ vorlag. Der Selbsttest mit absichtlich
manipulierter Zukunft prüft das automatisch: Wird der Backtest dadurch besser, liegt ein
Lookahead-Bias vor. → {ref:sec:handelsmaschine-zusammenfassung}
+:::
+::: {#gloss:gradient}
**Gradient**{idx:Gradient} — Vektor der partiellen Ableitungen einer Funktion; er zeigt in
Richtung des steilsten Anstiegs. Gradientenbasierte Verfahren wie SLSQP nutzen ihn, um sich
schrittweise an ein Optimum heranzutasten. → {ref:sec:qp-nlp-lernziele}
+:::
+::: {#gloss:graphen}
+**Graphen**{idx:Graphen} — Mathematische Struktur aus Knoten und Kanten; das Modellierungsmittel
+für Netzwerke, Touren und Flüsse. {ref:kap:graphen} behandelt Min-Cost-Flow, Matching und das
+Vehicle Routing Problem als Graphanwendungen. → {ref:kap:graphen}
+:::
+
+::: {#gloss:gurobi}
**Gurobi**{idx:Gurobi} — Kommerzieller Hochleistungssolver für LP, MILP und QP. Im Buch nicht
vorausgesetzt, aber als Referenzpunkt genannt: Über die Modellierungsschicht lässt er sich meist
durch Ändern eines einzigen Arguments einsetzen. → {ref:sec:milp-branch-and-bound}
+:::
## H
+::: {#gloss:herfindahl-index}
**Herfindahl-Index**{idx:Herfindahl-Index} — Summe der quadrierten Anteile; Maß für
Konzentration. Sein Kehrwert ist die *effektive Titelzahl* und beantwortet, auf wie viele gleich
große Positionen ein Portfolio praktisch verteilt ist. →
[Projektwerkstatt](59_Projektwerkstatt.md)
+:::
+::: {#gloss:hierarchische-relaxation}
**Hierarchische Relaxation**{idx:Hierarchische Relaxation} — Gegenmittel gegen Unlösbarkeit im
Betrieb: Jede harte Bedingung, die im Notfall gebrochen werden darf, erhält eine teure
Schlupfvariable. Der Solver liefert dann immer einen Plan und zeigt zugleich präzise, wo es
klemmt. → {ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:highs}
**HiGHS**{idx:HiGHS} — Offener Hochleistungssolver für LP, MILP und QP. Backend von
`scipy.optimize.linprog` und CVXPY, direkt ansprechbar über `highspy`. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
## I
+::: {#gloss:iis-irreducible-infeasible-subset}
**IIS (Irreducible Infeasible Subset)**{idx:IIS (Irreducible Infeasible Subset)} — Minimale
Menge von Nebenbedingungen, die zusammen unlösbar sind, deren echte Teilmengen aber alle lösbar
wären. Das präziseste Ergebnis, das eine Unlösbarkeitsdiagnose liefern kann. →
{ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:implikation}
**Implikation**{idx:Implikation (Modellierungsmuster)} — Modellierungsmuster für „wenn A, dann
B“: $y_B \ge y_A$. Die Umkehrung gilt dabei ausdrücklich nicht — $B$ darf auch ohne $A$ gesetzt
sein. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:importfehler-zwischen-ortools-und-highspy}
**Importfehler zwischen ortools und highspy**{idx:Importfehler (ortools und highspy)} — Absturz
beim Import beider Bibliotheken im selben Prozess: Sie bringen je eine eigene HiGHS-Kopie mit,
deren C++-Symbole kollidieren. Abhilfe ist die Trennung in getrennte Prozesse — CVXPY zieht ein
installiertes `highspy` dabei ungefragt mit hinein. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:infeasibility}
**Infeasibility**{idx:Infeasibility} — Zustand eines Modells ohne jede zulässige Lösung. Im
Betrieb über Schlupfvariablen mit hohem Strafgewicht aufzufangen, in der Diagnose über den
Deletion Filter einzugrenzen. → {ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:instabile-loesung}
**Instabile Lösung**{idx:Instabile Lösung (Fehlerbild)} — Fehlerbild, bei dem winzige
Datenänderungen die Lösung völlig umwerfen. Meist eine Folge schlechter Konditionierung; die
Konditionszahl zeigt es an, die Ruiz-Equilibrierung behebt es. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:interior-point-verfahren}
**Interior-Point-Verfahren**{idx:Interior-Point-Verfahren} — Lösungsverfahren, das sich durch
das Innere des zulässigen Bereichs zum Optimum bewegt, statt Ecken abzulaufen. Bei sehr großen
Modellen oft schneller als der Simplex, liefert dafür keine Basislösung und damit weniger
brauchbare Sensitivitätsinformationen. → {ref:sec:oekosystem-die-zwei-schichten-architektur}
+:::
+::: {#gloss:intervallvariable}
**Intervallvariable**{idx:Intervallvariable} — Variable mit Start, Dauer und Ende, die in CP-SAT
eine Aktivität beschreibt. Sie ist die Voraussetzung für Überschneidungsverbote und
Ressourcenprofile. → {ref:sec:cpsat-globale-constraints-die-bausteine-von-cp}
+:::
## J
+::: {#gloss:jensensche-ungleichung}
**Jensensche Ungleichung**{idx:Jensensche Ungleichung} — Für konvexe Funktionen gilt
$\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])$. Die formale Grundlage des Fluchs des Durchschnitts: Mit
Mittelwerten zu rechnen unterschätzt systematisch die erwarteten Kosten. →
{ref:sec:unsicherheit-der-fluch-des-durchschnitts}
+:::
+::: {#gloss:job-shop-scheduling}
**Job-Shop-Scheduling**{idx:Job-Shop-Scheduling} — Ablaufplanung, bei der mehrere Aufträge in je
eigener Reihenfolge über dieselben Maschinen laufen. Klassisches Anwendungsfeld von CP-SAT;
Zielgröße ist meist der Makespan. → {ref:sec:cpsat-intervallvariablen-job-shop-scheduling}
+:::
## K
+::: {#gloss:kanonische-standardform}
**Kanonische Standardform**{idx:Kanonische Standardform} — Einheitliche Matrixschreibweise eines
LP als $\min \mathbf{c}^\top\mathbf{x}$ unter den Bedingungen $\mathbf{A}\mathbf{x} \le
\mathbf{b}$ und $\mathbf{x} \ge 0$. Jedes lineare Modell lässt sich darauf bringen — Solver
erwarten genau diese Form. → {ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:kapazitaetsvektor}
**Kapazitätsvektor**{idx:Kapazitätsvektor} — Der Vektor $\mathbf{b}$ der rechten Seiten in
$\mathbf{A}\mathbf{x} \le \mathbf{b}$. Seine Einträge sind die verfügbaren Ressourcen, und die
zugehörigen Schattenpreise sagen, was eine zusätzliche Einheit davon wert wäre. →
{ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:kardinalitaetsbeschraenkung}
**Kardinalitätsbeschränkung**{idx:Kardinalitätsbeschränkung} — Obergrenze für die Anzahl
gleichzeitig aktiver Entscheidungen, formuliert über die Summe der zugehörigen Binärvariablen.
Typisch für „höchstens zehn Titel im Depot“. →
{ref:sec:milp-modellierungstricks-big-m-und-logische-bedingungen}
+:::
+::: {#gloss:karush-kuhn-tucker-bedingungen-kkt}
**Karush-Kuhn-Tucker-Bedingungen (KKT)**{idx:KKT-Bedingungen} — Notwendige
Optimalitätsbedingungen restringierter Probleme: Stationarität, primale und duale Zulässigkeit
sowie komplementärer Schlupf. Bei konvexen Problemen sind sie zugleich hinreichend und damit ein
vollständiger Optimalitätsnachweis. → {ref:sec:qp-nlp-die-karush-kuhn-tucker-bedingungen}
+:::
+::: {#gloss:kohaerentes-risikomass}
**Kohärentes Risikomaß**{idx:Kohärentes Risikomaß} — Risikomaß mit den vier Eigenschaften
Monotonie, Subadditivität, positive Homogenität und Translationsinvarianz. Der CVaR erfüllt sie,
der VaR nicht — dessen Verletzung der Subadditivität kann Diversifikation rechnerisch bestrafen.
→ {ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:kombinatorische-explosion}
**Kombinatorische Explosion**{idx:Kombinatorische Explosion} — Überproportionales Wachstum der
Zahl möglicher Lösungen mit der Problemgröße. Sie ist der Grund, warum systematisches
Ausprobieren schon bei kleinen Instanzen aussichtslos wird. →
{ref:sec:einfuehrung-warum-ausprobieren-scheitert-mit-eigener-rechnung}
+:::
+::: {#gloss:komplementaerer-schlupf}
**Komplementärer Schlupf**{idx:Komplementärer Schlupf} — Bedingung $s_i \cdot y_i = 0$: Eine
Nebenbedingung ist entweder aktiv (kein Schlupf) oder ihr Schattenpreis verschwindet.
Wirtschaftlich gelesen: Nur knappe Ressourcen haben einen Preis. →
{ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:konditionszahl}
**Konditionszahl**{idx:Konditionszahl} — Verhältnis von größtem zu kleinstem Singulärwert einer
Matrix. Sie misst, wie stark kleine Datenänderungen die Lösung verzerren können; hohe Werte
kündigen numerische Probleme an, lange bevor der Solver etwas meldet. →
{ref:sec:finanzdaten-praxis-datenpipeline-mit-korrekter-spaltenreihenfolge}
+:::
+::: {#gloss:konische-optimierung}
**Konische Optimierung**{idx:Konische Optimierung (SOCP, SDP)} — Erweiterung der linearen
Optimierung auf Kegelbedingungen. Der Second-Order Cone (SOCP) erfasst Normen und damit
Risikoschranken, die semidefinite Variante (SDP) Bedingungen an Matrizen. Beide bleiben konvex
und damit global lösbar. → {ref:sec:unsicherheit-chance-constraints}
+:::
+::: {#gloss:konvexitaet}
**Konvexität**{idx:Konvexität} — Eigenschaft, bei der jede Verbindungsstrecke zweier Punkte
innerhalb der Menge bzw. unterhalb des Funktionsgraphen liegt. Bei konvexen Problemen ist jedes
lokale Optimum global — die eigentliche Grenze zwischen leicht und schwer lösbar. →
{ref:sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und}
+:::
+::: {#gloss:korn-transformation}
**Korn-Transformation**{idx:Korn-Transformation} — Umformung der nicht-konvexen
Sharpe-Ratio-Maximierung in ein konvexes quadratisches Programm durch Homogenisierung. Sie macht
aus einem Bruch eine Skalierung und damit aus einem unlösbaren ein lösbares Problem. →
{ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:kostenzerlegung}
**Kostenzerlegung**{idx:Kostenzerlegung} — Aufschlüsselung eines Zielfunktionswerts in seine
Bestandteile — etwa Alpha-Ertrag, Risikoprämie und Transaktionskostenstrafe. Sie beantwortet die
Frage „woraus besteht dieses Ergebnis?“ und ist ein Baustein der Erklärbarkeit. →
{ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:kovarianzmatrix}
**Kovarianzmatrix**{idx:Kovarianzmatrix} — Matrix der paarweisen Kovarianzen aller Titel. Sie
steuert im Markowitz-Modell den Diversifikationseffekt und ist bei vielen Titeln und wenigen
Beobachtungen notorisch schlecht konditioniert. →
{ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:kumulative-ressource}
**Kumulative Ressource**{idx:Kumulative Ressource} — Ressource mit begrenzter Kapazität, die
mehrere Aktivitäten gleichzeitig nutzen können — etwa vier Monteure. In CP-SAT über
`AddCumulative` ausgedrückt. → {ref:anhang:modellierungsmuster}
+:::
## L
+::: {#gloss:lagrange-funktion}
**Lagrange-Funktion**{idx:Lagrange-Funktion} — Zielfunktion zuzüglich der mit Multiplikatoren
gewichteten Nebenbedingungen. Ihre Stationaritätsbedingung ist die erste der KKT-Bedingungen und
die Brücke zwischen restringierter und freier Optimierung. →
{ref:sec:qp-nlp-die-karush-kuhn-tucker-bedingungen}
+:::
+::: {#gloss:lagrange-multiplikator}
**Lagrange-Multiplikator**{idx:Lagrange-Multiplikator} — Gewicht, mit dem eine Nebenbedingung in
die Lagrange-Funktion eingeht. Sein Optimalwert ist der Schattenpreis der Bedingung. →
{ref:sec:qp-nlp-die-karush-kuhn-tucker-bedingungen}
+:::
+::: {#gloss:large-neighborhood-search-lns}
**Large Neighborhood Search (LNS)**{idx:Large Neighborhood Search} — Metaheuristik, die
wiederholt einen größeren Teil der aktuellen Lösung zerstört und exakt neu aufbaut. Sie
verbindet die Reichweite heuristischer Sprünge mit der Genauigkeit eines Solvers auf dem
Teilproblem. → {ref:sec:metaheuristiken-lns}
+:::
+::: {#gloss:laufzeitexplosion}
**Laufzeitexplosion**{idx:Laufzeitexplosion (Fehlerbild)} — Fehlerbild, bei dem ein Modell nach
einer kleinen Erweiterung nicht mehr fertig rechnet. Häufige Ursachen sind ein zu großes $M$,
fehlende Symmetriebrechung und eine schwache Relaxation. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:ledoit-wolf-shrinkage}
**Ledoit-Wolf-Shrinkage**{idx:Ledoit-Wolf-Shrinkage} — Schrumpfung der Stichprobenkovarianz in
Richtung eines strukturierten Ziels; der optimale Mischungsfaktor wird analytisch bestimmt.
`scikit-learn` verwendet die skalierte Einheitsmatrix als Ziel. →
{ref:sec:finanzdaten-ledoit-wolf-shrinkage}
+:::
+::: {#gloss:lexikografische-optimierung}
**Lexikografische Optimierung**{idx:Lexikografische Optimierung} — Mehrzielverfahren mit
strenger Rangfolge: Das wichtigste Ziel wird optimiert, sein Wert festgeschrieben, dann das
zweitwichtigste — und so fort. Kein Ziel darf zugunsten eines nachrangigen verschlechtert
werden. → {ref:sec:mehrziel-epsilon}
+:::
+::: {#gloss:lineare-programmierung-lp}
**Lineare Programmierung (LP)**{idx:Lineare Programmierung (LP)} — *Linear Programming* —
Optimierung einer linearen Zielfunktion unter linearen Nebenbedingungen mit kontinuierlichen
Variablen. Die am besten verstandene Problemklasse des Operations Research: in Polynomialzeit
lösbar und mit vollständiger Dualitätstheorie. →
{ref:sec:lp-die-standardform-und-schlupfvariablen}
+:::
+::: {#gloss:linopy}
**Linopy**{idx:Linopy} — Modellierungsbibliothek, die Optimierungsmodelle über beschriftete
Achsen aufbaut (`xarray`-Stil). Ihre Stärke sind große, regelmäßig indizierte Modelle wie
Energiesystemrechnungen. → {ref:sec:oekosystem-pyomo-linopy}
+:::
+::: {#gloss:linprog}
**linprog**{idx:linprog} — Funktion `scipy.optimize.linprog` zum Lösen linearer Programme. Sie
erwartet das Modell in kanonischer Standardform als Matrizen und benutzt seit SciPy 1.11
standardmäßig HiGHS als Backend. → {ref:sec:lp-die-vorzeichenfalle-bei-schattenpreisen}
+:::
+::: {#gloss:logarithmische-rendite}
**Logarithmische Rendite**{idx:Logarithmische Rendite} — Stetige Rendite als Logarithmus des
Preisverhältnisses. Sie ist **über die Zeit** additiv und deshalb die richtige Wahl für
Zeitreihenaggregation — nicht aber für die Gewichtung im Portfolio. →
{ref:sec:finanzdaten-diskrete-und-logarithmische-renditen}
+:::
+::: {#gloss:lokales-minimum}
**Lokales Minimum**{idx:Lokales Minimum} — Punkt, der in seiner Umgebung optimal ist, global
aber übertroffen werden kann. Bei nichtkonvexen Problemen liefern gradientenbasierte Verfahren
nur solche Punkte; ein Multistart macht das Problem sichtbar. →
{ref:sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und}
+:::
+::: {#gloss:lookahead-bias}
**Lookahead-Bias**{idx:Lookahead-Bias} — Fehler, bei dem Informationen in eine Entscheidung
einfließen, die zum Entscheidungszeitpunkt noch nicht vorlagen. Die häufigste Ursache für
Backtests, die zu gut aussehen, um wahr zu sein. →
{ref:sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens}
+:::
+::: {#gloss:lp-relaxation}
**LP-Relaxation**{idx:LP-Relaxation} — Das ganzzahlige Modell ohne die Ganzzahligkeitsforderung.
Ihre Lösung ist schnell zu berechnen und liefert eine Schranke: Besser als der Relaxationswert
kann die ganzzahlige Lösung nicht werden. Genau darauf beruht das Abschneiden im
Branch-and-Bound. → {ref:sec:milp-branch-and-bound}
+:::
## M
+::: {#gloss:makespan}
**Makespan**{idx:Makespan} — Gesamtdurchlaufzeit eines Ablaufplans: der Zeitpunkt, zu dem die
letzte Aktivität endet. Standardzielgröße im Job-Shop-Scheduling. →
{ref:sec:cpsat-intervallvariablen-job-shop-scheduling}
+:::
+::: {#gloss:mape}
**MAPE** — *Mean Absolute Percentage Error* — mittlerer absoluter prozentualer Prognosefehler.
Beliebt, weil einheitenfrei, aber unbrauchbar bei Werten nahe null. → {ref:sec:prognose-naht}
+:::
+::: {#gloss:marchenko-pastur-gesetz}
**Marchenko-Pastur-Gesetz**{idx:Marchenko-Pastur-Gesetz} — Ergebnis der Zufallsmatrizentheorie
über die Eigenwertverteilung von Stichproben-Kovarianzmatrizen. Es zeigt, dass die kleinsten
Eigenwerte systematisch unterschätzt und die größten überschätzt werden — die theoretische
Wurzel des Error-Maximizer-Effekts. → {ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:markowitz-modell-mean-variance}
**Markowitz-Modell (Mean-Variance)**{idx:Markowitz-Modell (Mean-Variance)} — Rahmenwerk, das ein
Portfolio über das Zusammenspiel von erwarteter Rendite und Kovarianz bewertet. Es begründete
die moderne Portfoliotheorie und ist zugleich das Lehrbeispiel für die Empfindlichkeit gegenüber
Schätzfehlern. → {ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:marktauswirkung-market-impact}
**Marktauswirkung (Market Impact)**{idx:Marktauswirkung} — Preisverschlechterung, die eine
eigene Order durch ihr Volumen auslöst. Sie wächst überproportional mit der
Handelsgeschwindigkeit und ist der Grund, große Orders über die Zeit zu strecken. →
{ref:sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung}
+:::
+::: {#gloss:matplotlib}
**Matplotlib** — Standardbibliothek für statische Diagramme in Python. Im Buch erzeugt sie alle
Abbildungen der Druckfassung; die interaktiven Fassungen der Website stammen von Plotly. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:matrixform}
**Matrixform**{idx:Matrixform} — Kompakte Schreibweise eines Modells über Vektoren und Matrizen
statt einzelner Gleichungen. Sie ist nicht nur kürzer, sondern die Form, die Solver tatsächlich
entgegennehmen. → {ref:sec:fundament-zusammenfassung}
+:::
+::: {#gloss:matrix-vektor-produkt}
**Matrix-Vektor-Produkt**{idx:Matrix-Vektor-Produkt} — Rechenoperation $\mathbf{A}\mathbf{x}$,
die jede Zeile der Matrix mit dem Vektor multipliziert. In der Optimierung berechnet sie den
Ressourcenverbrauch aller Nebenbedingungen in einem Schritt. →
{ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:maximin-fairness}
**Maximin-Fairness**{idx:Maximin-Fairness} — Zielsetzung „der am schlechtesten Gestellte soll
möglichst gut dastehen“. Modelliert über eine Hilfsvariable $t$ mit $t \le f_i(\mathbf{x})$ für
alle $i$ und Maximierung von $t$. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:maximum-drawdown}
**Maximum Drawdown**{idx:Maximum Drawdown} — Größter prozentualer Rückgang vom bisherigen
Höchststand. Anders als die Volatilität misst er den tatsächlich erlittenen Verlustweg und ist
deshalb die Kennzahl, an der Anleger aussteigen. → {ref:sec:handelsmaschine-die-engine}
+:::
+::: {#gloss:mensch-in-der-schleife}
**Mensch in der Schleife**{idx:Mensch in der Schleife} — Betriebsprinzip, bei dem der Optimierer
**vorschlägt** und ein Mensch **entscheidet** — zumindest in der Einführungsphase. Das baut
Vertrauen auf und fängt Modellfehler ab, bevor sie wirksam werden. →
{ref:sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
+:::
+::: {#gloss:metaheuristik}
**Metaheuristik**{idx:Metaheuristik} — Übergeordnetes Suchschema wie Simulated Annealing,
Tabu-Suche oder genetische Algorithmen, das auf viele Problemtypen anwendbar ist. Es liefert
gute Lösungen ohne Optimalitätsbeweis und ist dort angebracht, wo exakte Verfahren an der Größe
scheitern. → {ref:sec:metaheuristiken-aufgabe}
+:::
+::: {#gloss:min-cost-flow-problem}
**Min-Cost-Flow-Problem**{idx:Min-Cost-Flow} — Kostengünstigster Transport durch ein Netzwerk
unter Kapazitäts- und Flusserhaltungsbedingungen. Die Modellmatrix ist total unimodular, weshalb
schon die LP-Lösung ganzzahlig ausfällt. → {ref:sec:graphen-graphen-als-modellsprache}
+:::
+::: {#gloss:mindestabnahmemenge}
**Mindestabnahmemenge**{idx:Mindestabnahmemenge} — Bedingung „entweder gar nichts oder
mindestens $m$“. Sie ist nicht linear und braucht eine Binärvariable: $m\,y \le x \le M\,y$. →
{ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:minlp}
**MINLP**{idx:MINLP} — *Mixed-Integer Nonlinear Programming* — Modelle mit ganzzahligen
Variablen **und** nichtlinearen Funktionen. Die schwierigste der Standardklassen: Sie vereint
die kombinatorische Explosion mit dem Problem lokaler Optima. → {ref:sec:qp-nlp-nichtkonvex}
+:::
+::: {#gloss:mip-gap}
**MIP-Gap**{idx:MIP-Gap} — Relativer Abstand zwischen der besten gefundenen Lösung und der
besten bekannten Schranke. Er beziffert, wie viel im schlechtesten Fall noch fehlt — ein Gap von
2 % heißt: höchstens 2 % besser ist überhaupt möglich. →
{ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:miqp}
**MIQP** — *Mixed-Integer Quadratic Programming* — quadratische Zielfunktion mit ganzzahligen
Variablen. Der Fall, der entsteht, sobald ein Portfoliomodell eine Obergrenze für die Zahl der
Titel bekommt. → [Notation und Abkürzungen](01_Notation_und_Abkuerzungen.md)
+:::
+::: {#gloss:monte-carlo-simulation}
**Monte-Carlo-Simulation**{idx:Monte-Carlo-Simulation} — Erzeugung vieler Zufallsszenarien, um
Kennzahlen empirisch zu schätzen. Sie **bewertet** eine gegebene Entscheidung, optimiert aber
nicht — der Unterschied wird in der Praxis häufig übersehen. →
{ref:sec:unsicherheit-monte-carlo-simulation}
+:::
+::: {#gloss:mse}
**MSE** — *Mean Squared Error* — mittlerer quadratischer Fehler. Das Standardmaß der
Prognosegüte; es bestraft große Abweichungen überproportional und ist nicht dasselbe wie geringe
Entscheidungskosten. → [Notation und Abkürzungen](01_Notation_und_Abkuerzungen.md)
+:::
+::: {#gloss:mtz-formulierung}
**MTZ-Formulierung**{idx:MTZ-Formulierung} — Miller-Tucker-Zemlin-Bedingungen, die über
Rangvariablen Kurzzyklen ausschließen. Kompakt in der Zahl der Nebenbedingungen, dafür mit
schwacher Relaxation — der klassische Zielkonflikt bei Tourenmodellen. →
{ref:sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
+:::
+::: {#gloss:multi-objective-optimization}
**Multi-Objective Optimization**{idx:Multi-Objective Optimization} — Optimierung mit mehreren,
konkurrierenden Zielen. Statt einer besten Lösung gibt es eine Pareto-Front; die Auswahl daraus
ist eine Entscheidung, keine Rechnung. →
{ref:sec:praxisfallen-weiterfuehrende-literatur-und-roadmap}
+:::
## N
+::: {#gloss:nebenbedingung-constraint}
**Nebenbedingung (Constraint)**{idx:Nebenbedingung} — Gleichung oder Ungleichung, die zulässige
von unzulässigen Lösungen trennt. Harte Bedingungen müssen erfüllt sein, weiche werden über
Strafterme lediglich bepreist. → {ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:newsvendor-problem}
**Newsvendor-Problem**{idx:Newsvendor-Problem} — Klassisches Einperiodenmodell: Wie viel
bestellt man bei unsicherer Nachfrage, wenn Überbestand und Fehlmenge unterschiedlich teuer
sind? Die Lösung ist ein Quantil des Nachfragemodells, nicht ihr Erwartungswert. →
{ref:sec:unsicherheit-schnellstart}
+:::
+::: {#gloss:nichtlineare-programmierung-nlp}
**Nichtlineare Programmierung (NLP)**{idx:Nichtlineare Programmierung (NLP)} — *Nonlinear
Programming* — Problemklasse mit nichtlinearer Ziel- oder Nebenbedingungsfunktion. Ohne
Konvexität liefern Verfahren wie SLSQP nur lokale Optima. →
{ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
+:::
+::: {#gloss:nichtueberlappung}
**Nichtüberlappung**{idx:Nichtüberlappung} — Bedingung, dass sich Aktivitäten auf derselben
Ressource zeitlich nicht überschneiden. In CP-SAT über `AddNoOverlap` auf Intervallvariablen
ausgedrückt. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:np-schwer}
**NP-schwer**{idx:NP-schwer} — Komplexitätsklasse von Problemen, für die kein Algorithmus mit
polynomialer Laufzeit bekannt ist. Praktisch heißt das: Die Lösungszeit kann mit der
Problemgröße explodieren — nicht aber, dass jede Instanz schwer wäre. →
{ref:sec:milp-branch-and-bound}
+:::
+::: {#gloss:numpy}
**NumPy** — Grundbibliothek für numerisches Rechnen in Python. Sie liefert das Array, mit dem
Vektoren und Matrizen eines Modells überhaupt erst kompakt und schnell aufgebaut werden können.
→ {ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
## O
+::: {#gloss:operations-research-or}
**Operations Research (OR)**{idx:Operations Research} — Disziplin, die reale
Entscheidungsprobleme in mathematische Modelle überführt und mit exakten oder heuristischen
Algorithmen löst. Sie beantwortet nicht „was wird geschehen?“, sondern „was sollen wir tun?“. →
{ref:sec:einfuehrung-was-ist-operations-research-wirklich}
+:::
+::: {#gloss:optimalitaetsprinzip}
**Optimalitätsprinzip**{idx:Optimalitätsprinzip} — Grundsatz von Bellman: Jede Teilpolitik einer
optimalen Politik ist ihrerseits optimal für den erreichten Zustand. Ohne ihn wäre die
Rückwärtsinduktion nicht zulässig. →
{ref:sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip}
+:::
+::: {#gloss:or-tools}
**OR-Tools**{idx:OR-Tools} — Optimierungssammlung von Google mit CP-SAT, einer
Routing-Bibliothek und Anbindungen an LP/MILP-Solver. Im Buch die erste Wahl für Reihenfolge-,
Zuordnungs- und Tourenprobleme. → {ref:sec:einfuehrung-lernziele}
+:::
+::: {#gloss:overfitting}
**Overfitting**{idx:Overfitting} — Anpassung eines Modells an Zufälligkeiten der Trainingsdaten
statt an die zugrunde liegende Struktur. Im Backtesting die Regel, nicht die Ausnahme — deshalb
der Walk-Forward-Test. → {ref:sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens}
+:::
## P
+::: {#gloss:pandas}
**pandas** — Bibliothek für tabellarische Daten mit Zeit- und Beschriftungsindex. Im Buch der
übliche Weg von Roh- zu Modelldaten, besonders in den Finanzkapiteln. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:parameter}
**Parameter**{idx:Parameter (Modell)} — Feststehende Größe eines Modells — Kosten, Kapazitäten,
Bedarfe. Parameter werden nicht optimiert, sondern gegeben; ihre Unsicherheit ist der Gegenstand
der stochastischen und robusten Optimierung. →
{ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:pareto-front}
**Pareto-Front**{idx:Pareto-Front} — Menge aller Lösungen, bei denen kein Ziel verbessert werden
kann, ohne ein anderes zu verschlechtern. Sie zeigt den Zielkonflikt als Kurve, statt ihn in
einer Gewichtung zu verstecken. → {ref:sec:mehrziel-dominanz}
+:::
+::: {#gloss:pivotisierung}
**Pivotisierung**{idx:Pivotisierung} — Basiswechsel im Simplex-Verfahren: Eine Variable verlässt
die Basis, eine andere tritt ein. Jeder Pivotschritt entspricht dem Weiterwandern zur
benachbarten Ecke des Polyeders. → {ref:sec:lp-der-simplex-algorithmus-schritt-fuer-schritt}
+:::
+::: {#gloss:plotly}
**Plotly** — Bibliothek für interaktive Diagramme im Browser. Die Website des Buchs bindet damit
Gantt-Diagramme, Effizienzlinien und Pareto-Fronten zum Zoomen und Erkunden ein. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:polars}
**Polars**{idx:Polars} — Schnelle DataFrame-Bibliothek mit spaltenorientiertem Speicher und
verzögerter Auswertung. Im Buch als Alternative zu pandas dort erwähnt, wo der Modellaufbau
selbst zum Engpass wird. → {ref:sec:oekosystem-vektorisierung}
+:::
+::: {#gloss:polyeder}
**Polyeder**{idx:Polyeder} — Schnittmenge endlich vieler Halbräume — die geometrische Gestalt
des zulässigen Bereichs eines linearen Programms. Seine Ecken sind die Kandidaten für das
Optimum. → {ref:sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder}
+:::
+::: {#gloss:positiv-semi-definit}
**Positiv (semi-)definit**{idx:Positiv (semi-)definit} — Eigenschaft einer symmetrischen Matrix,
deren Eigenwerte alle $> 0$ (definit) bzw. $\ge 0$ (semidefinit) sind. Definit bedeutet streng
konvex und eine eindeutige Lösung, semidefinit konvex mit möglicherweise mehreren. →
{ref:sec:qp-nlp-das-quadratische-programm-qp}
+:::
+::: {#gloss:praeskriptive-analytik}
**Präskriptive Analytik**{idx:Präskriptive Analytik} — Analysestufe, die vorschreibt, welche
Handlung unter den gegebenen Bedingungen die beste ist. Sie folgt auf die deskriptive (was war?)
und die prädiktive Stufe (was wird?). →
{ref:sec:einfuehrung-was-ist-operations-research-wirklich}
+:::
+::: {#gloss:predict-then-optimize}
**Predict-then-Optimize**{idx:Predict-then-Optimize} — Zweistufiges Vorgehen: erst
prognostizieren, dann mit der Prognose optimieren. Der Bruch liegt dazwischen — das
Prognosemodell minimiert Vorhersagefehler, nicht Entscheidungskosten. → {ref:sec:prognose-naht}
+:::
+::: {#gloss:primales-problem}
**Primales Problem**{idx:Primales Problem} — Das ursprünglich formulierte Optimierungsproblem,
im Gegensatz zu seinem Dualproblem. Welches von beiden „primal“ heißt, ist Konvention: Das Duale
des Dualen ist wieder das Primale. → {ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:pydantic}
**pydantic** — Bibliothek zur Validierung von Datenstrukturen anhand von Typannotationen. Im
Buch prüft sie die Eingaben des Optimierungsdienstes, bevor ein Modell überhaupt gebaut wird. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:pymoo}
**pymoo** — Bibliothek für mehrkriterielle Optimierung mit evolutionären Verfahren. Sie liefert
Pareto-Fronten dort, wo sich die Ziele nicht sinnvoll skalarisieren lassen. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:pyomo}
**Pyomo**{idx:Pyomo} — Umfangreiche Modellierungssprache für Optimierung in Python, mit
Unterstützung für abstrakte Modelle, viele Solver und nichtlineare Formulierungen. Mächtiger,
aber ausführlicher als CVXPY. → {ref:sec:oekosystem-pyomo-linopy}
+:::
## Q
+::: {#gloss:q-learning}
**Q-Learning**{idx:Q-Learning} — Verfahren des bestärkenden Lernens, das den Wert von
Zustand-Aktion-Paaren aus Erfahrung schätzt, ohne ein Modell der Umgebung zu kennen. Verwandt
mit der dynamischen Programmierung, aber lernend statt rechnend. →
{ref:sec:dynamische-programmierung-der-fluch-der-dimensionalitaet}
+:::
+::: {#gloss:quadratische-programmierung-qp}
**Quadratische Programmierung (QP)**{idx:Quadratische Programmierung (QP)} — *Quadratic
Programming* — Optimierung einer quadratischen Zielfunktion unter linearen Nebenbedingungen. Bei
positiv semidefiniter Matrix konvex und damit zuverlässig global lösbar; die
Portfoliooptimierung ist der Standardfall. → {ref:sec:qp-nlp-das-quadratische-programm-qp}
+:::
## R
+::: {#gloss:rebalancing}
**Rebalancing**{idx:Rebalancing} — Periodische Rückführung des Portfolios auf die Zielgewichte.
Zu häufig kostet Gebühren, zu selten lässt die Gewichte driften — und die Termine müssen
Handelstage sein. → {ref:sec:handelsmaschine-rebalancing-termine-richtig-bestimmen}
+:::
+::: {#gloss:regime-shift}
**Regime-Shift**{idx:Regime-Shift} — Strukturbruch in den Daten, nach dem historisch geschätzte
Momente ihre Gültigkeit verlieren. Kein Modellfehler, sondern eine Eigenschaft der Welt —
abzufangen über kürzere Schätzfenster und robuste Formulierungen. →
{ref:sec:praxisfallen-die-fuenf-typischen-praxisfallen}
+:::
+::: {#gloss:reinforcement-learning}
**Reinforcement Learning**{idx:Reinforcement Learning} — Lernen einer Entscheidungsstrategie aus
Rückmeldungen der Umgebung. Es löst dieselbe Klasse mehrstufiger Probleme wie die dynamische
Programmierung, benötigt aber kein explizites Übergangsmodell. →
{ref:sec:dynamische-programmierung-der-fluch-der-dimensionalitaet}
+:::
+::: {#gloss:relaxation}
**Relaxation**{idx:Relaxation} — Absichtliches Weglassen einschränkender Forderungen —
typischerweise der Ganzzahligkeit —, um eine schnell berechenbare Schranke zu gewinnen. Der Wert
der Relaxation begrenzt, wie gut die echte Lösung überhaupt werden kann. →
{ref:sec:milp-branch-and-bound}
+:::
+::: {#gloss:robuste-optimierung}
**Robuste Optimierung**{idx:Robuste Optimierung} — Auslegung auf den ungünstigsten Fall
innerhalb einer Unsicherheitsmenge. Sie verlangt keine Wahrscheinlichkeiten, sondern nur eine
Angabe darüber, wie weit die Parameter schwanken können. →
{ref:sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
+:::
+::: {#gloss:rockafellar-uryasev-theorem}
**Rockafellar-Uryasev-Theorem**{idx:Rockafellar-Uryasev-Theorem} — Ergebnis, das die
CVaR-Minimierung in eine konvexe Hilfsfunktion überführt. Es macht die Optimierung des
Tail-Risikos mit gewöhnlichen linearen Solvern möglich. →
{ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:routing-bibliothek}
**Routing-Bibliothek**{idx:Routing-Bibliothek} — Spezialisierter Teil von OR-Tools für Touren-
und Reihenfolgeprobleme. Sie arbeitet heuristisch mit Zeitbudget — „optimal“ wird hier in der
Regel nicht bewiesen. → {ref:sec:graphen-zusammenfassung}
+:::
+::: {#gloss:rucksackproblem}
**Rucksackproblem**{idx:Rucksackproblem} — Auswahl von Gegenständen mit Wert und Gewicht unter
einer Kapazitätsgrenze. Das Schulbeispiel der ganzzahligen Optimierung und Modell für jede
Budgetauswahl. → {ref:sec:milp-beispiel-das-rucksackproblem}
+:::
+::: {#gloss:rueckwaertsinduktion}
**Rückwärtsinduktion**{idx:Rückwärtsinduktion} — Lösungsrichtung der dynamischen Programmierung:
von der letzten Stufe rückwärts. Nur dort steht der Wert eines Zustands ohne Kenntnis der
Zukunft fest, und von dort aus lässt sich jede frühere Stufe auf bereits bekannte Werte
zurückführen. → {ref:sec:dynamische-programmierung-lernziele}
+:::
+::: {#gloss:ruiz-equilibrierung}
**Ruiz-Equilibrierung**{idx:Ruiz-Equilibrierung} — Skalierungsverfahren, das Zeilen und Spalten
einer Matrix iterativ so multipliziert, dass alle Beträge in derselben Größenordnung liegen.
Nach wenigen Durchläufen liegen die Maxima bei 1 — die einfachste wirksame Gegenmaßnahme gegen
schlechte Konditionierung. → {ref:sec:fundament-kondition}
+:::
+::: {#gloss:ruestzeit}
**Rüstzeit**{idx:Rüstzeit} — Umschaltzeit zwischen zwei aufeinanderfolgenden Aufträgen auf
derselben Maschine. Sie ist reihenfolgeabhängig und macht aus einer einfachen Zuordnung ein
Reihenfolgeproblem. → {ref:anhang:modellierungsmuster}
+:::
## S
+::: {#gloss:sat-boolean-satisfiability}
**SAT (Boolean Satisfiability)**{idx:SAT (Boolean Satisfiability)} — Problem, für eine
aussagenlogische Formel eine erfüllende Belegung zu finden. Das erste als NP-vollständig
bewiesene Problem — und die Sprache, in die CP-SAT jedes Modell übersetzt. →
{ref:sec:cpsat-ein-anderes-denkmodell}
+:::
+::: {#gloss:satz-von-birkhoff-und-von-neumann}
**Satz von Birkhoff und von Neumann**{idx:Satz von Birkhoff und von Neumann} — Jede doppelt
stochastische Matrix ist eine Mischung von Permutationsmatrizen. Er erklärt, warum das
Zuordnungsproblem schon als lineares Programm ganzzahlige Lösungen liefert. →
{ref:sec:graphen-bipartites-matching-das-zuordnungsproblem}
+:::
+::: {#gloss:schattenpreis}
**Schattenpreis**{idx:Schattenpreis} — Optimalwert einer Dualvariablen: Um wie viel ändert sich
der Zielwert, wenn die zugehörige Ressource um eine Einheit erweitert wird? Er gilt nur lokal —
innerhalb des Bereichs, den die Sensitivitätsanalyse ausweist. →
{ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:schaetzfehler}
**Schätzfehler**{idx:Schätzfehler} — Abweichung geschätzter Größen wie Renditen oder Kovarianzen
von ihren wahren Werten. In der Portfoliooptimierung nicht ein Randproblem, sondern das
Hauptproblem. → {ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:scheduling}
+**Scheduling**{idx:Scheduling} — Zuweisung von Tätigkeiten an Maschinen oder Personen unter
+Ressourcen- und Reihenfolgebedingungen. {ref:kap:cpsat} behandelt es mit CP-SAT
+(Intervallvariablen), {ref:kap:metaheuristiken} mit Simulated Annealing und LNS. →
+{ref:kap:cpsat}
+:::
+
+::: {#gloss:schlupfvariable}
**Schlupfvariable**{idx:Schlupfvariable} — Nichtnegative Hilfsvariable, die eine Ungleichung in
eine Gleichung überführt. Ihr Wert zeigt die ungenutzte Reserve; ist er null, ist die Bedingung
bindend. → {ref:sec:lp-die-standardform-und-schlupfvariablen}
+:::
+::: {#gloss:schnittebenen-cutting-planes}
**Schnittebenen (Cutting Planes)**{idx:Schnittebenen} — Zusätzliche gültige Ungleichungen, die
gebrochene LP-Lösungen abschneiden, ohne einen einzigen ganzzahligen Punkt zu verlieren. Sie
verschärfen die Relaxation und verkleinern damit den Suchbaum. → {ref:sec:milp-branch-and-bound}
+:::
+::: {#gloss:scikit-learn}
**scikit-learn** — Bibliothek für maschinelles Lernen. Im Buch für Prognosemodelle und für die
Ledoit-Wolf-Shrinkage der Kovarianzmatrix eingesetzt. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:scip}
**SCIP**{idx:SCIP} — Akademischer Solver für gemischt-ganzzahlige lineare und nichtlineare
Programme, über OR-Tools und Pyomo ansprechbar. Für Forschung und Lehre frei nutzbar;
kommerzieller Einsatz verlangt eine eigene Lizenz. → {ref:sec:milp-branch-and-bound}
+:::
+::: {#gloss:scipy}
**SciPy** — Wissenschaftliche Standardbibliothek von Python. Ihr Teilmodul `scipy.optimize`
enthält mit `linprog`, `milp` und `minimize` die Einstiegswerkzeuge für lineare, ganzzahlige und
nichtlineare Optimierung. → {ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
+::: {#gloss:semikontinuierliche-variable}
**Semikontinuierliche Variable**{idx:Semikontinuierliche Variable} — Variable, die entweder null
ist oder in einem Intervall $[m, M]$ liegt. Sie erfasst Mindestlosgrößen und wird über eine
Binärvariable modelliert. → {ref:sec:milp-modellierungstricks-big-m-und-logische-bedingungen}
+:::
+::: {#gloss:sensitivitaetsanalyse}
**Sensitivitätsanalyse**{idx:Sensitivitätsanalyse} — Untersuchung, in welchem Bereich sich
Koeffizienten und Kapazitäten ändern dürfen, ohne die Struktur der Optimallösung zu verändern.
Sie macht aus einer Zahl eine Aussage über ihre Belastbarkeit. →
{ref:sec:lp-praxisfall-sensitivitaetsanalyse-mit-korrekten-schattenpreisen}
+:::
+::: {#gloss:shannon-entropie}
**Shannon-Entropie**{idx:Shannon-Entropie} — Maß für die Gleichverteiltheit einer Verteilung.
Als Zielfunktion eingesetzt erzwingt sie Streuung, ohne eine bestimmte Aufteilung vorzugeben. →
{ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
+:::
+::: {#gloss:sharpe-ratio}
**Sharpe Ratio**{idx:Sharpe-Ratio} — Überrendite über den risikofreien Zins je Einheit
Volatilität. Ihre Maximierung ist zunächst nicht konvex, wird es aber durch die
Korn-Transformation. → {ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:simplex-algorithmus}
**Simplex-Algorithmus**{idx:Simplex-Algorithmus} — Verfahren von Dantzig, das von Ecke zu Ecke
des Polyeders wandert und den Zielfunktionswert dabei monoton verbessert. Im schlechtesten Fall
exponentiell, in der Praxis außerordentlich zuverlässig. →
{ref:sec:lp-der-simplex-algorithmus-schritt-fuer-schritt}
+:::
+::: {#gloss:simulated-annealing}
**Simulated Annealing**{idx:Simulated Annealing} — Metaheuristik nach dem Vorbild des
Metallabkühlens: Verschlechterungen werden anfangs häufig, später kaum noch akzeptiert. So
entkommt die Suche lokalen Optima, ohne die Richtung ganz zu verlieren. →
{ref:sec:metaheuristiken-annealing}
+:::
+::: {#gloss:skalarisierung}
**Skalarisierung**{idx:Skalarisierung} — Zusammenfassung mehrerer Ziele zu einer gewichteten
Summe. Bequem, aber mit zwei Haken: Die Gewichte sind Werturteile, und nicht jeder Punkt der
Pareto-Front lässt sich so erreichen. → {ref:sec:mehrziel-skalarisierung}
+:::
+::: {#gloss:skalarprodukt}
**Skalarprodukt**{idx:Skalarprodukt} — Summe der paarweisen Produkte zweier Vektoren,
$\mathbf{c}^\top\mathbf{x}$. In der Optimierung die kompakte Schreibweise jeder linearen
Zielfunktion. → {ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:slsqp}
**SLSQP**{idx:SLSQP} — *Sequential Least Squares Programming* — gradientenbasiertes Verfahren in
`scipy.optimize.minimize` für nichtlineare Probleme mit Nebenbedingungen. Es findet lokale
Optima; ohne Konvexität entscheidet der Startpunkt über das Ergebnis. →
{ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
+:::
+::: {#gloss:snapshot-prinzip}
**Snapshot-Prinzip**{idx:Snapshot-Prinzip} — Betriebsregel, nach der jeder Optimierungslauf auf
einem unveränderlichen Datenschnappschuss mit eigener Kennung arbeitet. Nur so ist ein Ergebnis
später reproduzierbar — und im Streitfall belegbar. →
{ref:sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
+:::
+::: {#gloss:solver}
+**Solver**{idx:Solver} — Softwarekomponente, die ein mathematisches Modell löst — also das
+Optimierungsproblem in eine Lösung übersetzt. {ref:kap:oekosystem} vergleicht die in diesem Buch
+verwendeten Solver (HiGHS, OR-Tools/CP-SAT, CVXPY) und ihre Stärken. → {ref:kap:oekosystem}
+:::
+
+::: {#gloss:solver-status}
**Solver-Status**{idx:Solver-Status} — Rückgabewert, mit dem ein Solver das Ergebnis einordnet:
`OPTIMAL` (bewiesen optimal), `FEASIBLE` (zulässig, aber unbewiesen), `INFEASIBLE` (keine
zulässige Lösung), `UNBOUNDED` (Zielwert unbeschränkt) und `UNKNOWN` (Abbruch ohne Aussage). Ihn
ungeprüft zu übergehen ist die häufigste Fehlerquelle im produktiven Einsatz. → [Notation und
Abkürzungen](01_Notation_und_Abkuerzungen.md)
+:::
+::: {#gloss:starker-dualitaetssatz}
**Starker Dualitätssatz**{idx:Starker Dualitätssatz} — Aussage, dass primales und duales Problem
im Optimum denselben Zielfunktionswert besitzen — bei linearen Programmen immer, sobald beide
lösbar sind. Er macht den Dualwert zu einem belastbaren Preis statt zu einer bloßen Schranke. →
{ref:sec:lp-dualitaet-und-schattenpreise}
+:::
+::: {#gloss:stichproben-kovarianzmatrix}
**Stichproben-Kovarianzmatrix**{idx:Stichproben-Kovarianzmatrix} — Aus historischen Daten
geschätzte Kovarianzmatrix. Bei weniger Beobachtungen als Titeln ist sie singulär und damit
nicht invertierbar; schon bei ähnlicher Größenordnung wird sie extrem instabil. →
{ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:stochastische-programmierung}
**Stochastische Programmierung**{idx:Stochastische Programmierung} — Optimierung unter
Unsicherheit mit bekannten Wahrscheinlichkeiten. In der zweistufigen Form wird zwischen
Entscheidungen vor und nach der Beobachtung unterschieden — genau der Unterschied, den der Fluch
des Durchschnitts einebnet. → {ref:sec:unsicherheit-zweistufige-stochastische-programmierung}
+:::
+::: {#gloss:strafkosten}
**Strafkosten**{idx:Strafkosten} — Kosten, mit denen die Verletzung einer weichen Bedingung in
der Zielfunktion belegt wird. Ihre Höhe ist eine Modellierungsentscheidung: Sie legt fest, wie
viel Regelverstoß was wert ist. → {ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:subadditivitaet}
**Subadditivität**{idx:Subadditivität} — Eigenschaft $\rho(A+B) \le \rho(A)+\rho(B)$:
Diversifikation darf das Risiko nicht erhöhen. Der VaR verletzt sie, weshalb er kein kohärentes
Risikomaß ist. → {ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:subtour}
**Subtour**{idx:Subtour} — Kurzzyklus, der nur einen Teil der Knoten enthält und das Depot
umgeht. Ohne eigene Bedingungen — etwa MTZ — liefert ein Tourenmodell solche Gebilde als
scheinbar optimale Lösung. → {ref:sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
+:::
+::: {#gloss:survivorship-bias}
**Survivorship-Bias**{idx:Survivorship-Bias} — Verzerrung durch die Auswahl nur derjenigen
Titel, die es bis heute gibt. Die ausgeschiedenen fehlen — und mit ihnen die schlechtesten
Verläufe. → {ref:sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens}
+:::
+::: {#gloss:symmetriebrechung}
**Symmetriebrechung**{idx:Symmetriebrechung} — Zusätzliche Ordnungsbedingungen, die verhindern,
dass der Solver gleichwertige Vertauschungen mehrfach durchsucht. Bei identischen Maschinen oder
Fahrzeugen oft der größte einzelne Laufzeitgewinn. → {ref:anhang:modellierungsmuster}
+:::
## T
+::: {#gloss:tabu-suche}
**Tabu-Suche**{idx:Tabu-Suche} — Metaheuristik, die zuletzt besuchte Lösungen für einige
Schritte sperrt. Die Sperrliste zwingt die Suche aus lokalen Optima heraus, statt zwischen zwei
Nachbarn zu pendeln. → {ref:sec:metaheuristiken-abgrenzung}
+:::
+::: {#gloss:tangentialportfolio}
**Tangentialportfolio**{idx:Tangentialportfolio} — Portfolio auf der Effizienzgrenze mit der
höchsten Sharpe Ratio — der Berührpunkt der Kapitalmarktlinie. Es reagiert besonders empfindlich
auf Renditeschätzfehler. → {ref:sec:markowitz-das-mean-variance-modell}
+:::
+::: {#gloss:technologiematrix}
**Technologiematrix**{idx:Technologiematrix} — Die Matrix $\mathbf{A}$ der
Verbrauchskoeffizienten. Ihr Eintrag $a_{ij}$ sagt, wie viel von Ressource $i$ eine Einheit von
Aktivität $j$ verbraucht. Zusammen mit dem Kapazitätsvektor $\mathbf{b}$ bildet sie die
Nebenbedingungen $\mathbf{A}\mathbf{x} \le \mathbf{b}$. →
{ref:sec:fundament-warum-ueberhaupt-vektoren-und-matrizen}
+:::
+::: {#gloss:totale-unimodularitaet}
**Totale Unimodularität**{idx:Totale Unimodularität} — Eigenschaft einer Matrix, bei der jede
quadratische Teilmatrix die Determinante $0$, $+1$ oder $-1$ hat. Folge: Alle Ecken des
Polyeders sind ganzzahlig — Ganzzahligkeit muss gar nicht gefordert werden. →
{ref:sec:graphen-bipartites-matching-das-zuordnungsproblem}
+:::
+::: {#gloss:transaktionskosten}
**Transaktionskosten**{idx:Transaktionskosten} — Beim Umschichten anfallende Kosten aus
Gebühren, Spread und Marktauswirkung. Über eine $L_1$-Strafe modellierbar, die zugleich den
Turnover begrenzt. → {ref:sec:cvar-transaktionskosten-ueber-die-l-1-norm}
+:::
+::: {#gloss:trickle-flow}
**Trickle Flow**{idx:Trickle Flow} — Fehlerbild bei zu großem $M$: Ein verschwindend kleiner
Schaltwert — etwa $4 \cdot 10^{-8}$ — gilt dem Solver als null, lässt aber multipliziert mit
einem riesigen $M$ einen großen Fluss durch. Das Lager liefert, ohne offiziell geöffnet zu sein,
und die Fixkosten fehlen in der Rechnung. → {ref:sec:loesungen-milp}
+:::
+::: {#gloss:tsp-traveling-salesperson-problem}
**TSP (Traveling Salesperson Problem)**{idx:TSP (Traveling Salesperson Problem)} — Suche nach
der kürzesten Rundreise durch $n$ Orte. Das bekannteste NP-schwere Problem und der Ausgangspunkt
aller Tourenmodelle. → {ref:sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
+:::
+::: {#gloss:turnover-umschlag}
**Turnover (Umschlag)**{idx:Turnover (Umschlag)} — Summe der absoluten Gewichtsänderungen einer
Umschichtung. Die Größe, an der Transaktionskosten hängen — und die Kennzahl, mit der man
Rebalancing-Regeln vergleicht. → {ref:sec:cvar-transaktionskosten-ueber-die-l-1-norm}
+:::
+::: {#gloss:twap}
**TWAP**{idx:TWAP} — *Time-Weighted Average Price* — Ausführungsstrategie, die eine Order
gleichmäßig über die Zeit verteilt. Einfache Vergleichsgröße für jede optimierte Ausführung. →
{ref:sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung}
+:::
## U
+::: {#gloss:umruestkosten}
**Umrüstkosten**{idx:Umrüstkosten (Modellierungsmuster)} — Kosten, die beim Wechsel zwischen
zwei Produkten anfallen. Sie hängen vom Vorgänger ab und erzwingen deshalb Binärvariablen über
Paare statt über einzelne Aufträge. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:unbounded}
**Unbounded**{idx:Unbounded (Fehlerbild)} — Zustand, in dem der Zielwert unbeschränkt verbessert
werden kann. Fast immer ein Modellfehler — eine fehlende Kapazitätsgrenze oder ein vergessenes
Vorzeichen —, nicht ein besonders gutes Ergebnis. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:ungarischer-algorithmus}
**Ungarischer Algorithmus**{idx:Ungarischer Algorithmus} — Kombinatorisches Verfahren für das
Zuordnungsproblem mit polynomialer Laufzeit. Es löst exakt, was als allgemeines MILP deutlich
teurer wäre. → {ref:sec:graphen-zusammenfassung}
+:::
+::: {#gloss:unsicherheitsmenge}
**Unsicherheitsmenge**{idx:Unsicherheitsmenge} — Vorab definierter Bereich möglicher
Parameterwerte, gegen dessen ungünstigstes Element eine robuste Lösung abgesichert wird. Ihre
Größe steuert unmittelbar den Preis der Robustheit. →
{ref:sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
+:::
+::: {#gloss:unsinniges-ergebnis}
**Unsinniges Ergebnis**{idx:Unsinniges Ergebnis (Fehlerbild)} — Fehlerbild, bei dem der Solver
zwar `OPTIMAL` meldet, die Lösung aber offensichtlich unbrauchbar ist. Dann ist nicht die
Rechnung falsch, sondern das Modell: Es beschreibt eine andere Wirklichkeit als die gemeinte. →
{ref:anhang:fehlerdiagnose}
+:::
## V
+::: {#gloss:value-at-risk-var}
**Value at Risk (VaR)**{idx:Value at Risk (VaR)} — Verlustschwelle, die mit vorgegebener
Wahrscheinlichkeit nicht überschritten wird. Sie sagt nichts über die Verlusthöhe dahinter und
ist nicht subadditiv — beides Gründe für den Übergang zum CVaR. →
{ref:sec:cvar-value-at-risk-und-conditional-value}
+:::
+::: {#gloss:vehicle-routing-problem-vrp}
**Vehicle Routing Problem (VRP)**{idx:Vehicle Routing Problem (VRP)} — Verallgemeinerung des TSP
auf mehrere Fahrzeuge mit Depot und Kapazitätsgrenzen. In der Variante mit Zeitfenstern (VRPTW)
ist es der praktische Standardfall der Tourenplanung. Sich kreuzende Touren sind hier kein
Fehler: Die Zeitfenster können die Reihenfolge erzwingen. →
{ref:sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern}
+:::
+::: {#gloss:verdaechtig-guter-backtest}
**Verdächtig guter Backtest**{idx:Verdächtig guter Backtest (Fehlerbild)} — Fehlerbild der
Finanzkapitel: eine Strategie, die im Rückblick zu gut abschneidet. Die Prüfreihenfolge lautet
Lookahead, Survivorship, Kosten, Data Snooping — in dieser Reihenfolge, weil sie nach Häufigkeit
sortiert ist. → {ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:verhaeltnis-bedingung}
**Verhältnis-Bedingung**{idx:Verhältnis-Bedingung} — Anforderung der Art „höchstens 30 % aus
einer Quelle“. Sie wird linear, sobald man sie als $x_i \le 0{,}3 \sum_j x_j$ schreibt statt als
Bruch. → {ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:vertauschte-spalten}
**Vertauschte Spalten**{idx:Vertauschte Spalten (Fehlerbild)} — Fehlerbild, bei dem Daten und
Modell unterschiedliche Reihenfolgen annehmen. Es rechnet widerspruchsfrei und liefert dennoch
Unsinn — deshalb gehört die Spaltenreihenfolge abgesichert, nicht angenommen. →
{ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:volatilitaet}
**Volatilität**{idx:Volatilität} — Standardabweichung der Renditen, üblicherweise auf ein Jahr
skaliert. Sie misst Schwankung in beide Richtungen und behandelt Gewinne damit wie Verluste. →
{ref:sec:finanzdaten-diskrete-und-logarithmische-renditen}
+:::
+::: {#gloss:vorrangbeziehung}
**Vorrangbeziehung**{idx:Vorrangbeziehung} — Bedingung „Aktivität A muss vor Aktivität B
abgeschlossen sein“, formuliert als $\text{Ende}_A \le \text{Start}_B$. Der Grundbaustein jeder
Ablaufplanung. → {ref:anhang:modellierungsmuster}
+:::
## W
+::: {#gloss:walk-forward-backtest}
**Walk-Forward-Backtest**{idx:Walk-Forward-Backtest} — Rollierende Auswertung, bei der Parameter
stets nur auf Vergangenheitsdaten geschätzt und auf dem unmittelbar folgenden Zeitraum getestet
werden. Die einzige Backtest-Bauform, die Lookahead strukturell ausschließt. →
{ref:sec:handelsmaschine-die-engine}
+:::
+::: {#gloss:warm-start}
**Warm-Start**{idx:Warm-Start} — Übergabe einer bekannten Lösung als Startpunkt. Bei
wiederholten, ähnlichen Läufen — etwa täglicher Neuplanung — oft der größte Zeitgewinn
überhaupt. → {ref:sec:milp-gap}
+:::
+::: {#gloss:weiche-grenze-mit-strafkosten}
**Weiche Grenze mit Strafkosten**{idx:Weiche Grenze mit Strafkosten} — Modellierungsmuster, das
eine Schranke überschreitbar macht und die Überschreitung über eine Schlupfvariable in der
Zielfunktion bepreist. So bleibt das Modell lösbar und zeigt trotzdem, wo es klemmt. →
{ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:widerspruechliche-solver}
**Widersprüchliche Solver**{idx:Widersprüchliche Solver (Fehlerbild)} — Fehlerbild, bei dem zwei
Solver verschiedene Lösungen melden. Bei gleichem Zielwert sind es Alternativoptima und beide
haben recht; bei verschiedenem Zielwert liegt ein Modell- oder Toleranzproblem vor. →
{ref:anhang:fehlerdiagnose}
+:::
+::: {#gloss:wurzel-zeit-regel}
**Wurzel-Zeit-Regel**{idx:Wurzel-Zeit-Regel} — Skalierung der Volatilität mit $\sqrt{T}$. Sie
gilt streng nur für Standardabweichungen unabhängiger Größen ohne Drift — für VaR und CVaR ist
sie lediglich eine grobe Näherung. → {ref:sec:cvar-implementierung-cvar-portfolio-mit-reibung}
+:::
## Y
+::: {#gloss:yfinance}
**yfinance** — Bibliothek zum Abruf historischer Kursdaten. Die einzige Abhängigkeit des Buchs,
die eine Internetverbindung braucht — vier Programme nutzen sie. →
{ref:sec:oekosystem-die-werkzeuge-im-vergleich}
+:::
## Z
+::: {#gloss:zielfunktion}
**Zielfunktion**{idx:Zielfunktion} — Der zu minimierende oder maximierende Ausdruck, der die
Entscheidungsvariablen zu einer einzigen Bewertungszahl verdichtet. Ihre Formulierung
entscheidet, was das Modell überhaupt für gut hält. →
{ref:sec:einfuehrung-die-vier-universellen-bausteine-jedes-or}
+:::
+::: {#gloss:zufallsmatrizentheorie}
**Zufallsmatrizentheorie**{idx:Zufallsmatrizentheorie} — *Random Matrix Theory* — Theorie der
Eigenwertverteilung zufälliger Matrizen. Sie liefert den Maßstab dafür, welche Struktur in einer
geschätzten Kovarianzmatrix echt ist und welche bloßes Rauschen. →
{ref:sec:finanzdaten-das-schaetzfehler-problem}
+:::
+::: {#gloss:zulaessiger-bereich-feasible-region}
**Zulässiger Bereich (Feasible Region)**{idx:Zulässiger Bereich} — Menge aller Punkte, die
sämtliche Nebenbedingungen gleichzeitig erfüllen. Ist sie leer, ist das Modell unlösbar; ist sie
unbeschränkt, kann der Zielwert entgleiten. → {ref:anhang:literatur}
+:::
+::: {#gloss:zuordnungsproblem}
**Zuordnungsproblem**{idx:Zuordnungsproblem (1:1)} — Eins-zu-eins-Zuordnung zweier gleich großer
Mengen mit minimalen Kosten. Dank totaler Unimodularität schon als LP ganzzahlig lösbar. →
{ref:anhang:modellierungsmuster}
+:::
+::: {#gloss:zustandslosigkeit}
**Zustandslosigkeit**{idx:Zustandslosigkeit (OR-Plattform)} — Betriebsprinzip, nach dem der
rechnende Dienst keinen Zustand hält: Eingabe und Ausgabe sind Daten. Das macht Skalierung und
Wiederholung trivial. → {ref:sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
+:::
+::: {#gloss:zustandsraum}
**Zustandsraum**{idx:Zustandsraum} — Menge aller Zustände, die ein mehrstufiges
Entscheidungsproblem annehmen kann. Seine Größe entscheidet, ob dynamische Programmierung
praktikabel ist. → {ref:sec:dynamische-programmierung-der-fluch-der-dimensionalitaet}
+:::
+::: {#gloss:zwei-phasen-simplex-methode}
**Zwei-Phasen-Simplex-Methode**{idx:Zwei-Phasen-Simplex-Methode} — Vorgehen, um überhaupt eine
erste zulässige Ecke zu finden. Phase 1 minimiert mit Hilfsvariablen die Verletzung der
Nebenbedingungen; bleibt dabei ein Rest, ist das Modell unlösbar. Erst Phase 2 optimiert die
eigentliche Zielfunktion. → {ref:sec:loesungen-lp}
+:::
+::: {#gloss:zweischichtige-architektur}
**Zweischichtige Architektur**{idx:Zweischichtige Architektur} — Trennung in eine
Modellierungsschicht, in der man das Problem mathematiknah formuliert, und eine Solver-Schicht
aus hochoptimierten C++-Bibliotheken. Man schreibt Optimierungsalgorithmen im modernen OR nicht
selbst. → {ref:sec:oekosystem-die-zwei-schichten-architektur}
+:::
---
diff --git a/Operations_Research_mit_Python_Version_04/README.md b/Operations_Research_mit_Python_Version_04/README.md
index 73ecf42..47cfd62 100644
--- a/Operations_Research_mit_Python_Version_04/README.md
+++ b/Operations_Research_mit_Python_Version_04/README.md
@@ -157,4 +157,4 @@ Details zu den Lernpfaden stehen in [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_u
---
-*Autor / Herausgeber: Dieter Schlüter · Stand: 10. September 2026 v16.09*
+*Autor / Herausgeber: Dieter Schlüter · Stand: 10. September 2026 v17.38*
diff --git a/Operations_Research_mit_Python_Version_04/build_version_04.py b/Operations_Research_mit_Python_Version_04/build_version_04.py
index 5f44a17..42d985d 100644
--- a/Operations_Research_mit_Python_Version_04/build_version_04.py
+++ b/Operations_Research_mit_Python_Version_04/build_version_04.py
@@ -711,6 +711,17 @@ def reflow_markdown(text: str) -> str:
i += 1
continue
+ # Fenced-Div-Grenzen (::: {#...} / :::): wie Ueberschriften als
+ # Blockgrenze behandeln, sonst wuerde reflow die ::: mit dem
+ # folgenden Absatz zu einer Zeile verbinden — und Pandoc wuerde den
+ # div nicht mehr erkennen. Genutzt im Glossar (Anhang E): jeder
+ # Eintrag steht in einem ::: {#gloss:} ... :::-Block.
+ if content_stripped.startswith(":::"):
+ flush()
+ out.append(raw)
+ i += 1
+ continue
+
if _HR_RE.match(content_stripped):
flush()
out.append(raw)
@@ -1070,6 +1081,162 @@ def resolve_index(text: str) -> str:
return text
+# --- Glossar-Links: Fachbegriffe im Fließtext automatisch verlinken -------
+#
+# resolve_glossar() sucht im Fließtext nach Begriffen aus LINK_TEXTE (siehe
+# glossar_eintraege_04.py) und ersetzt das ERSTE Vorkommen je Begriff im
+# aktuellen Text durch einen Markdown-Link auf den Glossar-Eintrag. "Erste
+# Nennung" statt jedes Vorkommen: ein Begriff, der 576-mal vorkommt ('Solver'),
+# wuerde sonst jeden Absatz mit Links pflastern.
+#
+# Ausgeschlossen vom Link sind Code-Bloecke (```...```), Inline-Code (`...`),
+# Marken ({idx:...}, {ref:...}, {#...}), Markdown-Links [...](...) und
+# Ueberschriften — dort wuerde der Link stoen oder kaputtgehen.
+#
+# fuer_seite=False: Link als [Begriff](#gloss:slug) — fuer das Gesamtdokument.
+# fuer_seite=True: Link als [Begriff](anhang-glossar.md#gloss:slug) — fuer
+# Einzelseiten (seitenuebergreifend).
+#
+# Die manuelle Marke {gl:Begriff} wird ebenfalls aufgeloest — sie erzwingt
+# einen Link auch fuer Begriffe, die nicht in LINK_TEXTE stehen (z.B. weil
+# sie zu mehrdeutig fuer Auto-Linking sind, aber an dieser Stelle als
+# Fachbegriff gemeint sind).
+
+_GLOSSAR_TOKEN_RE = re.compile(
+ r'`[^`]*`' # Inline-Code `...`
+ r'|\{[^}]*\}' # Marken {...}
+ r'|\[[^\]]*\]\([^)]*\)' # Markdown-Links [text](url)
+)
+
+_GLOSSAR_GL_RE = re.compile(r'\{gl:([^}]+)\}')
+
+
+def resolve_glossar(text: str, fuer_seite: bool = False) -> str:
+ """Verlinkt Glossar-Begriffe im Fließtext automatisch (erste Nennung je
+ Begriff im uebergebenen Text) und loest {gl:}-Marken auf."""
+ from glossar_eintraege_04 import LINK_TEXTE
+
+ # {gl:Begriff}-Marken zuerst: erzwingen einen Link auch fuer Begriffe,
+ # die nicht auto-linkbar sind. 'Begriff' ist der Anzeigetext im Link;
+ # das Ziel ist der Glossar-Eintrag mit diesem Namen als Indexmarke (oder
+ # falls vorhanden, der Slug aus LINK_TEXTE).
+ gl_treffer = 0
+ def _gl_repl(m: re.Match) -> str:
+ nonlocal gl_treffer
+ begriff = m.group(1)
+ slug = LINK_TEXTE.get(begriff) or _glossar_slug_fuer(begriff)
+ ziel = (f'anhang-glossar.md#gloss:{slug}' if fuer_seite
+ else f'#gloss:{slug}')
+ gl_treffer += 1
+ return f'[{begriff}]({ziel}){{.glossar-link}}'
+ text = _GLOSSAR_GL_RE.sub(_gl_repl, text)
+ if gl_treffer:
+ print(f"Glossar-Links (manuell): {gl_treffer} Marke(n) aufgeloest.")
+
+ # Auto-Linking: laengste Link-Texte zuerst (damit 'Value at Risk' vor
+ # 'VaR' gematcht wird, falls beide im Text stuenden).
+ texte = sorted(LINK_TEXTE.items(), key=lambda x: -len(x[0]))
+
+ lines = text.split('\n')
+ in_code_block = False
+ code_fence = None
+ seen: set[str] = set()
+ treffer = 0
+
+ for i, line in enumerate(lines):
+ stripped = line.lstrip()
+ # Code-Block: nicht verlinken, nur Fence-Ende erkennen.
+ if in_code_block:
+ if stripped.startswith(code_fence):
+ in_code_block = False
+ continue
+ fence_m = re.match(r'^(```+|~~~+)', stripped)
+ if fence_m:
+ code_fence = fence_m.group(1)
+ in_code_block = True
+ continue
+ # Ueberschriften: nicht verlinken (wuerde Inhaltsverzeichnis stoen).
+ if _HEADING_RE.match(stripped):
+ continue
+
+ # Zeile in Token-Segmente (Code/Marke/Link) und Fließtext splitten;
+ # nur in Fließtext-Segmenten wird gesucht.
+ new_parts: list[str] = []
+ last = 0
+ for m in _GLOSSAR_TOKEN_RE.finditer(line):
+ seg = line[last:m.start()]
+ seg, t = _glossar_in_segment(seg, texte, seen, fuer_seite)
+ treffer += t
+ new_parts.append(seg)
+ new_parts.append(m.group(0)) # Token unverändert
+ last = m.end()
+ rest = line[last:]
+ rest, t = _glossar_in_segment(rest, texte, seen, fuer_seite)
+ treffer += t
+ new_parts.append(rest)
+ lines[i] = ''.join(new_parts)
+
+ if treffer:
+ print(f"Glossar-Links (auto): {treffer} Begriff(e) verlinkt.")
+ return '\n'.join(lines)
+
+
+def _glossar_slug_fuer(begriff: str) -> str:
+ """Slug fuer einen Begriff, der nicht in LINK_TEXTE steht (fuer {gl:}."""
+ from glossar_eintraege_04 import glossar_slug
+ return glossar_slug(begriff)
+
+
+def _glossar_in_segment(segment: str, texte: list[tuple[str, str]],
+ seen: set[str], fuer_seite: bool) -> tuple[str, int]:
+ """Sucht im Fließtext-Segment nach Glossar-Begriffen und ersetzt das
+ erste Vorkommen je Begriff durch einen Link.
+
+ Alle Link-Texte werden im Original-Segment gesucht (mit finditer, also
+ alle Vorkommen), dann nach Position sortiert und nicht-ueberlappend
+ ersetzt — laengste zuerst bei gleicher Position. So wird 'CP-SAT' als
+ Ganzes erkannt, bevor 'CP' (als Teilstring darin) ein eigenes Match
+ erzeugt. Wird ein Match wegen Ueberlappung übersprungen, wird das
+ naechste Vorkommen desselben Begriffs genommen (falls nicht ueberlappend).
+ """
+ matches: list[tuple[int, int, str, str]] = []
+ for link_text, slug in texte:
+ if link_text in seen:
+ continue
+ pat = re.compile(r'\b' + re.escape(link_text) + r'\b')
+ for m in pat.finditer(segment):
+ matches.append((m.start(), m.end(), link_text, slug))
+ # Alle Vorkommen sammeln — falls das erste wegen Ueberlappung
+ # mit einem laengeren Begriff (z.B. 'CP-SAT-Solver' blockiert
+ # 'Solver') uebersprungen wird, greift das naechste.
+ if not matches:
+ return segment, 0
+ # Nach Position aufsteigend, bei gleicher Position laengste zuerst —
+ # damit 'CP-SAT-Solver' (start=X, len=13) vor 'CP-SAT' (start=X, len=6)
+ # und 'CP' (start=X, len=2) kommt.
+ matches.sort(key=lambda x: (x[0], -(x[1] - x[0])))
+ result: list[str] = []
+ last_end = 0
+ treffer = 0
+ for start, end, link_text, slug in matches:
+ if start < last_end:
+ continue # ueberlappt mit einem bereits gesetzten laengeren Match
+ if link_text in seen:
+ continue # dieser Begriff wurde schon weiter oben im Segment
+ # verlinkt (zweiter nicht-ueberlappender Treffer waere moeglich,
+ # aber 'erste Nennung je Begriff' gilt global pro Datei, nicht pro
+ # Segment)
+ result.append(segment[last_end:start])
+ ziel = (f'anhang-glossar.md#gloss:{slug}' if fuer_seite
+ else f'#gloss:{slug}')
+ result.append(f'[{link_text}]({ziel}){{.glossar-link}}')
+ seen.add(link_text)
+ treffer += 1
+ last_end = end
+ result.append(segment[last_end:])
+ return ''.join(result), treffer
+
+
# Wird als letzter Textbaustein angehaengt - landet damit unmittelbar vor dem
# von Pandoc automatisch erzeugten \end{document}. \phantomsection sorgt
# dafuer, dass hyperref einen Sprunganker fuer den (mit \section* erzeugten,
@@ -1150,6 +1317,11 @@ def baue_markdown() -> str:
inhalt = inhalt.replace("Stand: {datum}", f"Stand: {datum}")
# Vorwort-Signatur 'Köln, im {vorwort_datum}' dynamisch setzen.
inhalt = inhalt.replace("{vorwort_datum}", aktuelles_monat_jahr())
+ # Glossar-Links pro Datei (erste Nennung je Begriff je Datei) —
+ # aber nicht im Glossar selbst: dort wuerde jeder Eintrag auf sich
+ # selbst verlinken.
+ if name != "94_Anhang_Glossar.md":
+ inhalt = resolve_glossar(inhalt, fuer_seite=False)
# Inhaltsverzeichnis vor dem Vorwort einfuegen (LaTeX-Rohblock)
if TOC_MARKE in inhalt and i == 0:
@@ -2293,6 +2465,9 @@ def baue_kapitel_seiten(html_verz: str, datei_seite: dict, labels: dict,
text = text.replace("{vorwort_datum}", aktuelles_monat_jahr())
text = reflow_markdown(text)
text = resolve_numbering_seite(text, eintrag["seite"], labels, label_seite)
+ # Glossar-Links — aber nicht im Glossar selbst (sonst Self-Links).
+ if name != "94_Anhang_Glossar.md":
+ text = resolve_glossar(text, fuer_seite=True)
text = resolve_index(text)
text, mit_plotly = resolve_plotly(text, fuer_html=True)
text = resolve_karte(text, fuer_html=True)
diff --git a/Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py b/Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py
index 43b5f8e..8d586a4 100755
--- a/Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py
+++ b/Operations_Research_mit_Python_Version_04/erzeuge_glossar_04.py
@@ -40,7 +40,7 @@ HIER = os.path.dirname(os.path.abspath(__file__))
BASIS = os.path.dirname(HIER)
sys.path.insert(0, HIER)
-from glossar_eintraege_04 import EINTRAEGE # noqa: E402
+from glossar_eintraege_04 import EINTRAEGE, glossar_slug # noqa: E402
ZIEL = os.path.join(HIER, "94_Anhang_Glossar.md")
@@ -70,11 +70,14 @@ BREITE = 96 # Zeilenbreite wie in den uebrigen Kapiteldateien
# hier eintragen.
NUR_IM_GLOSSAR = frozenset({
"Alternativoptima",
+ "Backtest",
"Binärvariable",
"CP-SAT",
"Calmar Ratio",
+ "DCP",
"Dualitätstheorie",
"Explainable OR",
+ "Graphen",
"Kanonische Standardform",
"Kohärentes Risikomaß",
"Lagrange-Multiplikator",
@@ -86,7 +89,9 @@ NUR_IM_GLOSSAR = frozenset({
"Rebalancing",
"Regime-Shift",
"Relaxation",
+ "Scheduling",
"Schätzfehler",
+ "Solver",
"Solver-Status",
"Transaktionskosten",
"Unsicherheitsmenge",
@@ -184,7 +189,13 @@ def baue() -> str:
zeilen += [f"## {buchstabe}", ""]
letzter = buchstabe
marke = "{idx:" + idx + "}" if idx else ""
- zeilen += [absatz(f"**{name}**{marke} — {text} → {ziel}"), ""]
+ slug = glossar_slug(name)
+ # ::: {#gloss:} ... ::: erzeugt im HTML einen
-
+ # Anker und im PDF eine \hypertarget-Marke; beide sind das Ziel der
+ # Auto-Links aus dem Fließtext.
+ zeilen += [f"::: {{#gloss:{slug}}}",
+ absatz(f"**{name}**{marke} — {text} → {ziel}"),
+ ":::", ""]
zeilen += ["---", "", SCHLUSS, ""]
return "\n".join(zeilen)
diff --git a/Operations_Research_mit_Python_Version_04/glossar_eintraege_04.py b/Operations_Research_mit_Python_Version_04/glossar_eintraege_04.py
index 576ba4d..ed0d5e8 100644
--- a/Operations_Research_mit_Python_Version_04/glossar_eintraege_04.py
+++ b/Operations_Research_mit_Python_Version_04/glossar_eintraege_04.py
@@ -32,6 +32,41 @@ ueberschreibt sie und bricht ab, wenn er eine Handaenderung bemerkt.
EINTRAEGE = [
+("Backtest", "Backtest",
+ "Rückblickender Test einer Strategie auf historischen Daten. Ein Backtest "
+ "ist nur so glaubwürdig wie seine Daten, seine Kostenannahme und seine "
+ "Trennung von Trainings- und Testzeitraum — "
+ "{ref:kap:handelsmaschine} zeigt die fünf Selbsttäuschungen, die ihn wertlos "
+ "machen.",
+ "{ref:kap:handelsmaschine}"),
+
+("DCP (Disciplined Convex Programming)", "DCP",
+ "Regelwerk von CVXPY, das jede Variable als konkav, konvex oder affin "
+ "ausweist und aus der Kombination der Bausteine die Konkavität/Konvexität "
+ "der Zielfunktion und der Nebenbedingungen erschließt. {ref:kap:qp-nlp} "
+ "erklärt es; ein `DCPError` meldet eine Verletzung.",
+ "{ref:kap:qp-nlp}"),
+
+("Graphen", "Graphen",
+ "Mathematische Struktur aus Knoten und Kanten; das Modellierungsmittel für "
+ "Netzwerke, Touren und Flüsse. {ref:kap:graphen} behandelt Min-Cost-Flow, "
+ "Matching und das Vehicle Routing Problem als Graphanwendungen.",
+ "{ref:kap:graphen}"),
+
+("Scheduling", "Scheduling",
+ "Zuweisung von Tätigkeiten an Maschinen oder Personen unter "
+ "Ressourcen- und Reihenfolgebedingungen. {ref:kap:cpsat} behandelt es mit "
+ "CP-SAT (Intervallvariablen), {ref:kap:metaheuristiken} mit Simulated "
+ "Annealing und LNS.",
+ "{ref:kap:cpsat}"),
+
+("Solver", "Solver",
+ "Softwarekomponente, die ein mathematisches Modell löst — also das "
+ "Optimierungsproblem in eine Lösung übersetzt. {ref:kap:oekosystem} "
+ "vergleicht die in diesem Buch verwendeten Solver (HiGHS, OR-Tools/CP-SAT, "
+ "CVXPY) und ihre Stärken.",
+ "{ref:kap:oekosystem}"),
+
("Absolutbetrag", "Absolutbetrag (Modellierungsmuster)",
"Modellierungsmuster für $|x-z|$: Der Betrag selbst ist nicht linear, lässt sich aber "
"durch eine Hilfsvariable $d$ mit den beiden Bedingungen $x-z \\le d$ und $z-x \\le d$ "
@@ -1418,3 +1453,96 @@ EINTRAEGE = [
"{ref:anhang:fehlerdiagnose}"),
]
+
+
+# --- Auto-Link-Infrastruktur ---------------------------------------------
+# Der Build verlinkt Glossar-Begriffe im Fließtext automatisch — die
+# {gl:}-Marke von Hand ist nur Ausnahme, nicht Regelfall. Diese beiden
+# Werte liefern die dazu noetigen Daten:
+#
+# glossar_slug(name) Eine stabile HTML-ID je Eintrag ('cp-sat', 'var'…).
+# Die Glossar-Datei traegt denselben Slug als
+# {#gloss:}-Anchor; der Link im Fließtext
+# zeigt dorthin.
+# LINK_TEXTE {Text_im_Buch: slug} — alles, was der Build durch
+# einen Link ersetzt. Generiert aus den EINTRAEGEN:
+# der Anzeigename je Eintrag, plus die Abkürzung,
+# falls der Name eine enthaelt (z.B. 'VaR' neben
+# 'Value at Risk').
+#
+# Einträge, die NICHT auto-verlinkt werden sollen, fehlen hier einfach.
+# 'Variable', 'Lösung', 'Matrix' etc. sind zu mehrdeutig als Fließtext und
+# werden nicht auto-verlinkt — wer sie verlinkt haben will, setzt {gl:}.
+
+import re as _re
+
+_UMLAUT = str.maketrans({"ä": "ae", "ö": "oe", "ü": "ue", "Ä": "Ae",
+ "Ö": "Oe", "Ü": "Ue", "ß": "ss"})
+
+
+def glossar_slug(name: str) -> str:
+ """ASCII-sicherer Slug fuer den Glossar-Anchor (wie die sec:-Labels)."""
+ s = name.translate(_UMLAUT).lower()
+ s = _re.sub(r"[^a-z0-9]+", "-", s).strip("-")
+ return s or "glossar"
+
+
+def _abkuerzung_aus_name(name: str) -> str | None:
+ """Extrahiert die Abkürzung aus einem Glossar-Namen.
+
+ Zwei Fälle:
+ * 'Value at Risk (VaR)' — Abkürzung im Klammerinhalt (kurz, überwiegend
+ Großbuchstaben, z.B. VaR, CVaR, KKT, MILP).
+ * 'EVPI (Expected Value ...)' — Abkürzung VOR der Klammer, wenn der
+ Teil vor der Klammer durchgehend Großbuchstaben ist (z.B. EVPI, IIS,
+ TSP, DTO, GIL, SAT).
+ """
+ m = _re.search(r"\(([^)]+)\)\s*$", name)
+ if not m:
+ return None
+ k = m.group(1).strip()
+ if len(k) <= 15 and sum(1 for c in k if c.isupper()) >= len(k) * 0.5:
+ return k
+ # Fall 2: Teil vor der Klammer ist die Abkürzung (z.B. 'EVPI (... )').
+ vor = name[: m.start()].strip()
+ if vor and len(vor) <= 8 and vor.isupper() and vor.isalpha():
+ return vor
+ return None
+
+
+# Begriffe, die zwar im Glossar stehen, aber im Fließtext NICHT
+# auto-verlinkt werden (zu mehrdeutig — 'Lösung' als Lösung eines
+# Gleichungssystems vs. Lösung einer Aufgabe vs. Optimallösung).
+# Wer sie verlinkt haben will, setzt {gl:Lösung} — das ist die Ausnahme.
+_NICHT_AUTO_LINKEN = frozenset({
+ "Binärvariable", "Entscheidungsvariable", "Intervallvariable",
+ "Schlupfvariable", "Semikontinuierliche Variable",
+ "Lösung", "Lösung (Modellierungsmuster)", "Instabile Lösung",
+ "Unsinniges Ergebnis", "Verdächtig guter Backtest",
+ "Matrix", "Matrixform", "Matrix-Vektor-Produkt",
+ "Nebenbedingung (Constraint)",
+ "Widersprüchliche Solver",
+ # Fehlerbilder: werden im Glossar geführt, aber im Fließtext nicht
+ # jedes Vorkommen verlinkt (sonst wird aus jedem 'Tippfehler' ein Link).
+})
+
+
+# Abkürzungen, die nicht auto-verlinkt werden — 'OR' ist 2 Zeichen und
+# taucht als Teilstring in 'OR-Tools' auf (Produktname, nicht die
+# Abkürzung für Operations Research). Wer 'OR' als Fachbegriff verlinken
+# will, setzt {gl:OR}. Die anderen 2-Zeichen-Abkürzungen (LP, QP, DP, CP)
+# kommen im Text nur als Fachbegriff vor und bleiben auto-verlinkt.
+_ZU_KURZ_ABK = frozenset({"OR"})
+
+
+LINK_TEXTE: dict[str, str] = {}
+for _name, _idx, _text, _ziel in EINTRAEGE:
+ if _name in _NICHT_AUTO_LINKEN:
+ continue
+ _slug = glossar_slug(_name)
+ # Anzeigename immer (z.B. 'Schattenpreis' -> 'schattenpreis').
+ LINK_TEXTE[_name] = _slug
+ # Abkürzung falls vorhanden (z.B. 'VaR' -> 'value-at-risk-var').
+ _abk = _abkuerzung_aus_name(_name)
+ if _abk and _abk not in _ZU_KURZ_ABK:
+ LINK_TEXTE[_abk] = _slug
diff --git a/bilder_04/titelseite.svg b/bilder_04/titelseite.svg
index be1ff69..21abb94 100644
--- a/bilder_04/titelseite.svg
+++ b/bilder_04/titelseite.svg
@@ -2444,7 +2444,7 @@ z
-
+
-
+
+
@@ -2596,10 +2647,10 @@ z
-
+
-
-
+
+
diff --git a/web_04/assets/site.css b/web_04/assets/site.css
index df75af5..2cde8a3 100644
--- a/web_04/assets/site.css
+++ b/web_04/assets/site.css
@@ -209,6 +209,18 @@ article h2[id$="-zusammenfassung"]::before { content: "📌 "; }
article a { color: var(--accent); text-decoration: none; }
article a:hover { text-decoration: underline; }
article a[target="_blank"]::after { content: " \2197"; font-size: .82em; color: var(--cyan); text-decoration: none; }
+/* Glossar-Links: dezent (gestrichelte Unterstreichung, nicht blau) —
+ sie sollen auffindbar, aber nicht wie Navigationslinks aussehen. */
+article a.glossar-link {
+ color: inherit;
+ text-decoration: underline dotted var(--accent);
+ text-decoration-thickness: 1px;
+ text-underline-offset: 2px;
+}
+article a.glossar-link:hover {
+ color: var(--accent);
+ text-decoration: underline solid var(--accent);
+}
article p { margin: .75em 0; }
article hr { border: 0; border-top: 1px solid var(--border); margin: 2.2em 0; }
article figure { margin: 2em 0; text-align: center; }