Sechster und siebter Fund: die letzten handgeschriebenen Nummern

Der Loesungsanhang trug 98 Marken der Bauart "**9.2 — Ansatz waehlen.**",
saemtlich aus Version 03, wo Unsicherheit Kapitel 9 war. Heute ist es
Kapitel 12, und die Aufgabe wird korrekt als "Aufgabe 12.2" gesetzt.

Das war keine Schoenheitsfrage: "Aufgabe 9.2" existiert wirklich - sie
gehoert zum Kapitel Metaheuristiken ("Zuggroesse und Temperatur"). Wer die
Loesung zu 9.2 nachschlug, landete beim falschen Thema. Beide bisherigen
Pruefungen liefen vorbei: Vor der Zahl steht kein Schluesselwort, und es ist
keine Tabellenzelle.

Zwei Messungen machten die Reparatur billig. In allen 15 nummerierten
Abschnitten gab es exakt so viele Loesungen wie Aufgaben, lueckenlos 1..n -
nur der Kapitelteil war falsch. Und die acht in Phase 3 ergaenzten Kapitel
machten es laengst richtig (**Titel.** ohne Nummer); der Anhang wurde also
nicht auf etwas Neues umgestellt, sondern auf das, was seine neueren Teile
schon taten.

nummeriere_marken() vergibt die Marken jetzt selbst. Der Praefix kommt NICHT
aus "# Anhang A:" - der Anhang ist eine Ueberschrift, seine Abschnitte
gehoeren aber zu 23 verschiedenen Kapiteln. Er kommt aus
{#sec:loesungen-X} -> {#kap:X}; diese Zuordnung gilt geprueft fuer alle 23.
Im Quelltext steht "**{loesung} — Titel.**".

Siebter Fund unterwegs: neun Verweise auf Denkfehler-Nummern. Saetze wie
"siehe Denkfehler 8.1" standen in alter Zaehlung - "8" war in Version 03 das
QP/NLP-Kapitel, heute ist es Graphen. Eine blosse Umnummerierung haette sie
stillschweigend woanders hin zeigen lassen; das Ziel wurde deshalb fuer jeden
einzelnen aus dem Zusammenhang bestimmt und auf {ref:sec:<kapitel>-denkfehler}
umgestellt. Einer steckte in einem Programm-Docstring und bekam nach Regel 12
den Kapitelnamen statt eines Verweises.

Eine bewusste Abweichung vom Plan: Der Denkfehler bekommt GAR KEINE Nummer.
Es gibt je Kapitel genau einen, die zweite Stelle waere immer .1, und er
steht ohnehin unter einer nummerierten Ueberschrift. Eine Nummer, die nie
variiert, holte nur die Fragilitaet zurueck, die hier beseitigt wird. Auch
das folgt den acht neuen Kapiteln, die ihren Denkfehler schon vorher nur mit
dem Titel des Raetsels ankuendigen.

--check bewacht jetzt alle Familien (Denkfehler N.M, Micro-Quiz N, **N.M —
im Anhang) und zaehlt zusaetzlich ab, dass jeder Loesungsabschnitt so viele
{loesung}-Marken hat wie sein Kapitel Aufgaben - das faengt eine vergessene
oder doppelte Loesung, was keine Textsuche leisten kann. Gegengetestet mit
vier kuenstlichen Fehlern auf einmal: alle vier gemeldet, nach dem Rueckbau
null.

Gegenprobe der Umstellung: Gesamtdokument vorher gesichert, nachher
verglichen. 168 geaenderte Zeilen, restlos einer Familie zuzuordnen - 118
Loesungsmarken (138 gesamt minus 20, die in den Kapiteln 1 bis 3 zufaellig
schon stimmten), 15 Anhang-Ueberschriften, 15 Kapitel-Callouts, 12
Micro-Quiz, 7 Zeilen mit Verweisen, 1 Docstring. Keine verschobene oder
inhaltlich veraenderte Zeile.

Ergebnis: 138 Aufgaben, 138 Loesungen, keine ohne Gegenstueck. Micro-Quiz
tragen die echten Kapitelnummern. 293 Abschnitte, 720 Querverweise, 327
Indexmarken, PDF unveraendert 725 Seiten, 33 pytest-Tests, 67 netzfreie
Programme fehlerfrei (die vier yfinance-Programme scheiterten am Rate-Limit
des Anbieters, nicht an dieser Aenderung).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dschlueter 2026-09-08 09:52:41 +02:00
commit cc990c2998
45 changed files with 1056 additions and 876 deletions

View file

@ -1605,7 +1605,7 @@ Ergebnis geschrieben nach: produktionsmix_ergebnis.xlsx
Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht stimmt. Gefährlich sind Programme, die fehlerfrei durchlaufen, einen Status `OPTIMAL` melden und eine Zahl liefern, die betriebswirtschaftlich Unsinn ist. Genau solche Fälle üben wir hier.
> **🐛 Finde den Denkfehler 1.1: Die Schreinerei verdoppelt ihren Gewinn**
> **🐛 Finde den Denkfehler: Die Schreinerei verdoppelt ihren Gewinn**
>
> Eine Kollegin hat das Schreinerei-Modell aus dem Kapitelanfang leicht umgebaut — sie fand die Schleife übersichtlicher als zwei einzelne `Add`-Aufrufe. Das Programm läuft, der Solver meldet `OPTIMAL`, und der Deckungsbeitrag ist plötzlich **18 600 €** statt 10 800 €. Der Chef ist begeistert. Sollte er das sein?
>
@ -2733,7 +2733,7 @@ und runden Sie Solver-Ergebnisse NIE ohne Toleranzpruefung.
## 2.9 Finde den Denkfehler {#sec:fundament-denkfehler}
> **🐛 Finde den Denkfehler 2.1: Die unauffällige Transposition**
> **🐛 Finde den Denkfehler: Die unauffällige Transposition**
>
> Ein Kollege überträgt ein Produktionsmodell in Matrixform. Zwei Produkte, zwei Ressourcen — die Technologiematrix ist quadratisch:
>
@ -3836,7 +3836,7 @@ Groesse Variante Aufbau Loesen Aufba
## 3.10 Finde den Denkfehler {#sec:oekosystem-denkfehler}
> **🐛 Finde den Denkfehler 3.1: Der Solver, der angeblich dreimal schneller ist**
> **🐛 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:
>
@ -5670,7 +5670,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
## 5.11 Finde den Denkfehler {#sec:lp-denkfehler}
> **🐛 Finde den Denkfehler 4.1: Die 380 000-Euro-Maschine**
> **🐛 Finde den Denkfehler: Die 380 000-Euro-Maschine**
>
> Ein Werksleiter lässt die Engpässe seiner Fertigung analysieren. Das Skript liefert für den Lackierofen einen Schattenpreis von **50 €/Stunde**. Er rechnet:
>
@ -5701,7 +5701,7 @@ brauchbare Aussage. Ein erfundener Einzelwert ist es nicht.
## 5.12 Micro-Quiz {#sec:lp-quiz}
> **❓ Micro-Quiz 4: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 5: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-lp).
>
@ -7032,7 +7032,7 @@ Hier wirkt der Hinweis: Faktor 2,2 bzw. 1,4. Beachten Sie aber, dass der Faktor
Die Warnung „$M$ so klein wie möglich“ wird meist mit der Laufzeit begründet: schwache Relaxation, mehr Knoten. Das stimmt — und ist die **harmlosere** Hälfte der Wahrheit.
> **🐛 Finde den Denkfehler 5.1: Elf Lager, die keine Fixkosten kosten**
> **🐛 Finde den Denkfehler: Elf Lager, die keine Fixkosten kosten**
>
> Ein Team plant Standorte: 12 mögliche Lager, 40 Kunden, Fixkosten je eröffnetem Lager. Für das Big-M in der Kopplung
>
@ -7310,7 +7310,7 @@ funktioniert immer.
## 6.11 Micro-Quiz {#sec:milp-quiz}
> **❓ Micro-Quiz 5: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 6: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-milp).
>
@ -8412,7 +8412,7 @@ genau die Information, die zur Fehlersuche noetig waere.
## 7.9 Finde den Denkfehler {#sec:cpsat-denkfehler}
> **🐛 Finde den Denkfehler 6.1: Die Betriebsvereinbarung, die niemanden interessiert**
> **🐛 Finde den Denkfehler: Die Betriebsvereinbarung, die niemanden interessiert**
>
> Ein Klinikteam lässt seinen Schichtplan optimieren. Zwei Wünsche kommen aus dem Betrieb:
>
@ -8620,7 +8620,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
## 7.10 Micro-Quiz {#sec:cpsat-quiz}
> **❓ Micro-Quiz 6: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 7: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-cpsat).
>
@ -9305,7 +9305,7 @@ if __name__ == "__main__":
## 8.7 Finde den Denkfehler {#sec:graphen-denkfehler}
> **🐛 Finde den Denkfehler 7.1: Die vergessene Dimension**
> **🐛 Finde den Denkfehler: Die vergessene Dimension**
>
> Eine Spedition lässt ihre Tagestouren optimieren: 16 Kunden, 4 Fahrzeuge zu je 10 Paletten, Gesamtbedarf 37 Paletten. Das erste Ergebnis begeistert alle — **326 km**. Nach einem Hinweis aus dem Fuhrpark wird das Modell überarbeitet; jetzt kommen **572 km** heraus, 75 % mehr. Der Auftraggeber ist verärgert: *„Ihre erste Version war doch viel besser.“*
>
@ -9553,7 +9553,7 @@ plausibel aussieht.
## 8.8 Micro-Quiz {#sec:graphen-quiz}
> **❓ Micro-Quiz 7: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 8: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-graphen).
>
@ -12677,7 +12677,7 @@ else:
## 11.8 Finde den Denkfehler {#sec:qp-nlp-denkfehler}
> **🐛 Finde den Denkfehler 8.1: Die Kovarianzmatrix aus dem Controlling**
> **🐛 Finde den Denkfehler: Die Kovarianzmatrix aus dem Controlling**
>
> Ein Analyst soll das Risiko eines Portfolios aus drei Anlagen minimieren. Die Korrelationen hat er aus drei verschiedenen Quartalsberichten zusammengetragen — jede für sich plausibel:
>
@ -12730,7 +12730,7 @@ else:
## 11.9 Micro-Quiz {#sec:qp-nlp-quiz}
> **❓ Micro-Quiz 8: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 11: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-qp-nlp).
>
@ -13873,7 +13873,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
## 12.9 Finde den Denkfehler {#sec:unsicherheit-denkfehler}
> **🐛 Finde den Denkfehler 9.1: Warum jedes Projekt zu spät fertig wird**
> **🐛 Finde den Denkfehler: Warum jedes Projekt zu spät fertig wird**
>
> Ein Projektleiter plant eine Umbaumaßnahme. Fünf Gewerke arbeiten **parallel** und unabhängig voneinander; fertig ist der Umbau, wenn das letzte fertig ist. Für jedes Gewerk liegt eine Schätzung vor: mindestens 6 Tage, wahrscheinlich 10, im schlechtesten Fall 18.
>
@ -13913,7 +13913,7 @@ Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er ko
## 12.10 Micro-Quiz {#sec:unsicherheit-quiz}
> **❓ Micro-Quiz 9: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 12: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-unsicherheit).
>
@ -14592,7 +14592,7 @@ Das Problem entsteht, sobald der Zustand **mehrere Dimensionen** hat:
Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder Misserfolg: **Was gehört in den Zustand?** Ist er zu klein, rechnet das Verfahren völlig korrekt — nur eben an einem anderen Problem.
> **🐛 Finde den Denkfehler 10.1: Der Zustand, der zu wenig weiß**
> **🐛 Finde den Denkfehler: Der Zustand, der zu wenig weiß**
>
> Eine Werkstatt plant vier Perioden. Der Bedarf beträgt 3, 1, 4, 2 Stück; produziert werden können höchstens 5 je Periode, gelagert höchstens 6 Stück.
>
@ -14635,7 +14635,7 @@ Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder
## 13.8 Micro-Quiz {#sec:dynamische-programmierung-quiz}
> **❓ Micro-Quiz 10: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 13: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-dynamische-programmierung).
>
@ -17843,7 +17843,7 @@ if __name__ == "__main__":
## 18.8 Finde den Denkfehler {#sec:finanzdaten-denkfehler}
> **🐛 Finde den Denkfehler 11.1: Das risikofreie Portfolio**
> **🐛 Finde den Denkfehler: Das risikofreie Portfolio**
>
> Ein Analyst stellt ein Portfolio aus **30 Titeln** zusammen. Für die Kovarianzmatrix nimmt er das letzte Quartal — bei Wochendaten sind das **20 Beobachtungen**. Dann minimiert er die Varianz.
>
@ -17913,7 +17913,8 @@ def minimales_risiko(kovarianz: np.ndarray) -> tuple[np.ndarray, float]:
exakt positiv semidefinit ist - rechne trotzdem.' Das ist hier bewusst so
gewaehlt, denn genau diese Eigenschaft wollen wir vorfuehren. Im
Produktivbetrieb waere die DCP-Fehlermeldung das Warnsignal, dem man
nachgehen muss (siehe Denkfehler 8.1).
nachgehen muss (siehe den Denkfehler im Kapitel Quadratische und
nichtlineare Optimierung).
"""
gewichte = cp.Variable(ANZAHL_ANLAGEN)
problem = cp.Problem(cp.Minimize(cp.quad_form(gewichte, cp.psd_wrap(kovarianz))),
@ -18036,7 +18037,7 @@ e 0: \mathbf{w}^ op\mathbf{S}\mathbf{w} = 0$ | „In genau diesen Richtungen mis
## 18.9 Micro-Quiz {#sec:finanzdaten-quiz}
> **❓ Micro-Quiz 11: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 18: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-finanzdaten).
>
@ -18727,7 +18728,7 @@ if __name__ == "__main__":
[Kapitel 18](#kap-finanzdaten) 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 12.1: Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio**
> **🐛 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.
>
@ -18933,7 +18934,7 @@ Drei praktische Konsequenzen:
## 19.9 Micro-Quiz {#sec:markowitz-quiz}
> **❓ Micro-Quiz 12: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 19: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-markowitz).
>
@ -19560,7 +19561,7 @@ if __name__ == "__main__":
## 20.8 Finde den Denkfehler {#sec:cvar-denkfehler}
> **🐛 Finde den Denkfehler 13.1: Das Risikobudget, das durch Diversifikation stieg**
> **🐛 Finde den Denkfehler: Das Risikobudget, das durch Diversifikation stieg**
>
> Eine Bank vergibt Risikobudgets je Abteilung. Zwei Kreditabteilungen halten je eine Unternehmensanleihe über 100 € Nominal mit **4 % Ausfallwahrscheinlichkeit** (bei Ausfall Totalverlust, sonst 2 € Kupon). Der Risikocontroller misst für jede einzeln:
>
@ -19596,7 +19597,7 @@ if __name__ == "__main__":
## 20.9 Micro-Quiz {#sec:cvar-quiz}
> **❓ Micro-Quiz 13: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 20: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-cvar).
>
@ -20297,7 +20298,7 @@ Ein guter Backtest ist eine **notwendige**, keine hinreichende Bedingung. Was zw
Die beiden übrigen Selbsttäuschungen sind anders gebaut. Sie entstehen nicht im Code, sondern in der **Arbeitsweise davor** — und hinterlassen im Programm keine Spur. Ein Backtest, der zwanzigmal nachjustiert wurde, sieht am Ende genauso sauber aus wie einer, der beim ersten Versuch funktioniert hat.
> **🐛 Finde den Denkfehler 14.1: Die Strategie mit dem hochsignifikanten Ergebnis**
> **🐛 Finde den Denkfehler: Die Strategie mit dem hochsignifikanten Ergebnis**
>
> Eine Analystin entwickelt über mehrere Wochen eine Handelsstrategie. Sie probiert verschiedene Signalfenster, Schwellenwerte, Filter und Haltedauern durch — insgesamt rund **hundert Varianten**. Die beste testet sie sauber: kein Lookahead, korrektes Universum, Transaktionskosten berücksichtigt.
>
@ -20505,7 +20506,7 @@ von allen Fehlversuchen genau einen: den erfolgreichsten.
## 21.10 Micro-Quiz {#sec:handelsmaschine-quiz}
> **❓ Micro-Quiz 14: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 21: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-handelsmaschine).
>
@ -22050,7 +22051,7 @@ Wenn Sie doch tiefer in die Methodik wollen, sind das die lohnendsten Richtungen
Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als **gar nichts**.
> **🐛 Finde den Denkfehler 15.1: Das Modell, das seit einem Jahr nicht mehr optimiert**
> **🐛 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.
>
@ -22324,7 +22325,7 @@ sichtbar wird, und gibt Zeit zum Handeln.
## 22.11 Micro-Quiz {#sec:praxisfallen-quiz}
> **❓ Micro-Quiz 15: Drei Fragen zum Selbstcheck**
> **❓ Micro-Quiz 22: Drei Fragen zum Selbstcheck**
>
> Genau eine Antwort ist jeweils richtig. Auflösung in [Anhang A](#quiz-loesung-praxisfallen).
>
@ -24215,7 +24216,7 @@ Probe: Mehl $0{,}5\cdot96 + 0{,}6\cdot70 = 48 + 42 = 90$ kg ✓ (voll ausgelaste
**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 1.1 — Die Schreinerei verdoppelt ihren Gewinn
### Finde den Denkfehler — Die Schreinerei verdoppelt ihren Gewinn
(a) **Nachrechnen.** Der Plan lautet 40 Tische und 150 Stühle: $40 \cdot 3 + 150 \cdot 1 = \mathbf{270}$ Montagestunden bei 150 verfügbaren, und $40 \cdot 6 + 150 \cdot 1 = \mathbf{390}$ m² Material bei 240 verfügbaren. Beide Vorräte sind fast doppelt überzogen — der Plan ist in der Werkstatt nicht ausführbar.
@ -24279,7 +24280,7 @@ def repariere(P):
```
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 2.1 — Die unauffällige Transposition
### 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.
@ -24289,7 +24290,7 @@ $$\mathbf{A}\mathbf{x} = \begin{pmatrix} 1\cdot5{,}6 + 2\cdot0{,}8 \\ 3\cdot5{,}
Die zweite Ressource ist um **47 % überzogen** (17,6 statt 12). Der Plan ist in der Werkstatt nicht ausführbar.
(c) **Warum „zu klein“ schlimmer ist als „zu groß“.** Ein unerwartet *hoher* Zielwert weckt Misstrauen — man rechnet nach (siehe Denkfehler 1.1). Ein leicht *niedrigerer* Wert wirkt dagegen wie ein normales, etwas enttäuschendes Ergebnis: Niemand prüft nach, warum die Optimierung „nur“ 20,80 € statt der erhofften 22 € bringt. Fehler, die sich als Bescheidenheit tarnen, überleben am längsten.
(c) **Warum „zu klein“ schlimmer ist als „zu groß“.** Ein unerwartet *hoher* Zielwert weckt Misstrauen — man rechnet nach (siehe [Abschnitt 1.10](#sec:einfuehrung-denkfehler)). Ein leicht *niedrigerer* Wert wirkt dagegen wie ein normales, etwas enttäuschendes Ergebnis: Niemand prüft nach, warum die Optimierung „nur“ 20,80 € statt der erhofften 22 € bringt. Fehler, die sich als Bescheidenheit tarnen, überleben am längsten.
(d) **Zwei Zeilen, die es aufgedeckt hätten.** Erstens beim Einlesen die Form gegen die Bedeutung prüfen, zweitens nach dem Lösen den Verbrauch gegen den Vorrat:
@ -24333,7 +24334,7 @@ Vorbeugend hilft außerdem, Matrizen nie positionsweise abzutippen, sondern übe
**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 3.1 — Der Solver, der angeblich dreimal schneller ist
### 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:
@ -24387,15 +24388,15 @@ Und die vierte, wichtigste Regel: **die Größe messen, die später auch läuft.
## A.4 Lösungen zu Kapitel „Vom Management-Wunsch zum Modell“ {#sec:loesungen-modellierung}
**Die fehlende Lesart.** Eine dritte Lesart: **„Der Umsatzanteil der Stammkunden darf nicht unter 30 % fallen."** Sie zielt weder auf einzelne Aufträge noch auf einzelne Kunden, sondern auf das Gesamtbild. Sie beschreibt einen Betrieb, dem die *Struktur* seines Geschäfts wichtig ist — etwa weil Stammkunden verlässlicher zahlen oder weil eine zu große Abhängigkeit von Neukunden als Risiko gilt. Sie lässt zu, dass ein einzelner Stammkunde in einem Quartal leer ausgeht, solange die Summe stimmt.
**4.1 — Die fehlende Lesart.** Eine dritte Lesart: **„Der Umsatzanteil der Stammkunden darf nicht unter 30 % fallen."** Sie zielt weder auf einzelne Aufträge noch auf einzelne Kunden, sondern auf das Gesamtbild. Sie beschreibt einen Betrieb, dem die *Struktur* seines Geschäfts wichtig ist — etwa weil Stammkunden verlässlicher zahlen oder weil eine zu große Abhängigkeit von Neukunden als Risiko gilt. Sie lässt zu, dass ein einzelner Stammkunde in einem Quartal leer ausgeht, solange die Summe stimmt.
Der Punkt der Aufgabe: Alle drei Lesarten sind vertretbar, und keine ist aus dem Satz ableitbar. Die Wahl trifft man — im Zweifel unbewusst.
**Frage 5 anwenden.** Wird die *durchschnittliche* Wartezeit minimiert, lohnt es sich, viele leichte Fälle schnell abzuarbeiten und die aufwendigen nach hinten zu schieben: Ein Fall mit vier Stunden Wartezeit zählt genauso viel wie vier Fälle mit einer Stunde. Der optimale Plan behandelt also bevorzugt Bagatellen und lässt schwere Fälle warten — medizinisch das Gegenteil des Gewollten.
**4.2 — Frage 5 anwenden.** Wird die *durchschnittliche* Wartezeit minimiert, lohnt es sich, viele leichte Fälle schnell abzuarbeiten und die aufwendigen nach hinten zu schieben: Ein Fall mit vier Stunden Wartezeit zählt genauso viel wie vier Fälle mit einer Stunde. Der optimale Plan behandelt also bevorzugt Bagatellen und lässt schwere Fälle warten — medizinisch das Gegenteil des Gewollten.
Das ist genau der Ertrag von Frage 5: Der Plan lässt sich *ablehnen*, und aus der Ablehnung folgt die richtige Zielgröße — etwa die maximale Wartezeit je Dringlichkeitsstufe, oder die Einhaltung von Zielzeiten je Triage-Kategorie.
**Der Kompromiss dazwischen.** Je Stammkunde eine Nebenbedingung: Die Summe der zugeteilten Stunden dieses Kunden muss mindestens die Hälfte seiner angefragten Stunden betragen.
**4.3 — Der Kompromiss dazwischen.** Je Stammkunde eine Nebenbedingung: Die Summe der zugeteilten Stunden dieses Kunden muss mindestens die Hälfte seiner angefragten Stunden betragen.
```python
for kunde in STAMMKUNDEN:
@ -24406,11 +24407,11 @@ for kunde in STAMMKUNDEN:
Erwartung: Das Ergebnis liegt zwischen Lesart A und B — die Regel bindet stärker als „ein Auftrag genügt", aber schwächer als „alle Aufträge". Der Lehrpunkt ist nicht die Zahl, sondern dass sich zwischen zwei Lesarten beliebig fein abstufen lässt, sobald man sie einmal als Ungleichung geschrieben hat.
**Zwei Ziele gleichzeitig.** Bei $\lambda = 0$ ergibt sich der DB-Plan (78 500 € DB), bei großem $\lambda$ der Umsatzplan (66 000 € DB). Dazwischen kippt es an genau einer Stelle — und zwar **sprunghaft**, nicht allmählich: Weil die Entscheidungsvariablen binär sind, gibt es nur endlich viele Pläne, und der Wechsel erfolgt, sobald ein anderer Plan den höheren gewichteten Wert hat.
**4.4 — Zwei Ziele gleichzeitig.** Bei $\lambda = 0$ ergibt sich der DB-Plan (78 500 € DB), bei großem $\lambda$ der Umsatzplan (66 000 € DB). Dazwischen kippt es an genau einer Stelle — und zwar **sprunghaft**, nicht allmählich: Weil die Entscheidungsvariablen binär sind, gibt es nur endlich viele Pläne, und der Wechsel erfolgt, sobald ein anderer Plan den höheren gewichteten Wert hat.
Der Kippwert sagt dem Betrieb etwas Konkretes: *„Erst wenn Ihnen ein Euro Umsatz mehr wert ist als λ Euro Deckungsbeitrag, ändert sich Ihr Plan."* Das ist eine Frage, die ein Kaufmann beantworten kann — anders als „welche Gewichtung hätten Sie gern?".
**Die Anforderung schreiben.** Eine tragfähige Anforderung enthält mindestens:
**4.5 — Die Anforderung schreiben.** Eine tragfähige Anforderung enthält mindestens:
* **Entscheidung:** Für jede Anfrage im Planungszeitraum: annehmen oder ablehnen.
* **Ziel:** Deckungsbeitrag maximieren (Umsatz minus Material).
@ -24466,21 +24467,21 @@ Im schwachen Monat ist die Vorgabe schlicht nicht erfüllbar — es gibt nicht g
## A.5 Lösungen zu Kapitel „Lineare Programmierung — Simplex, Dualität und Schattenpreise“ {#sec:loesungen-lp}
**4.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.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.
**4.2 — Vorzeichen.** Er hat die Zielfunktion für `linprog` negiert und die Dualwerte nicht zurückgedreht. Der korrekte Schattenpreis ist **+45 €**.
**5.2 — Vorzeichen.** Er hat die Zielfunktion für `linprog` negiert und die Dualwerte nicht zurückgedreht. Der korrekte Schattenpreis ist **+45 €**.
**4.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.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)$.
**4.4 — Duales Problem.** $\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.** $\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.
**4.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.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.
**4.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.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.
**4.7 — Phase 1.**`\index{Zwei-Phasen-Simplex-Methode}`{=latex} 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$.
**5.7 — Phase 1.**`\index{Zwei-Phasen-Simplex-Methode}`{=latex} 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$.
### Finde den Denkfehler 4.1 — Die 380 000-Euro-Maschine
### Finde den Denkfehler — Die 380 000-Euro-Maschine
(a) **Eine Ableitung gilt lokal.** $y_i = \partial Z^*/\partial b_i$ beschreibt die Steigung der Zielfunktion **am aktuellen Punkt**. Sie sagt: „Die *nächste* Stunde ist 50 € wert“ — nicht: „jede der nächsten 2 000 Stunden ist 50 € wert“. Der Werksleiter behandelt eine Ableitung wie eine Konstante und multipliziert sie mit einer großen Zahl. Genau daran scheitert die Rechnung.
@ -24532,21 +24533,21 @@ Das ist die allgemeine Regel für jede Sensitivitätsaussage über mehr als eine
## A.6 Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“ {#sec:loesungen-milp}
**5.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.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.
**5.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.
**5.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.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$.
**5.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$.**
**5.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.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.
**5.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.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.
**5.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.
**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.
### Finde den Denkfehler 5.1 — Elf Lager, die keine Fixkosten kosten
### Finde den Denkfehler — Elf Lager, die keine Fixkosten kosten
(a) **Warum die Zahl kleiner ist.** Ein größeres $M$ **lockert** die Nebenbedingung $\sum_j x_{ij} \le M y_i$. Der zulässige Bereich des Modells wird also größer, und in einem größeren Bereich kann das Minimum nur kleiner oder gleich bleiben. Bei einem *korrekt* formulierten Modell dürfte das aber gar nicht auffallen: Solange $y_i$ wirklich binär ist, ist $M \cdot 1 = M$ ohnehin größer als jede mögliche Liefermenge — die Lockerung wäre wirkungslos. Dass sich der Zielwert überhaupt ändert, ist deshalb schon der Beweis, dass $y_i$ **nicht** wirklich binär ist.
@ -24601,15 +24602,15 @@ Test 3 ist der wichtigste. Er vergleicht nicht Modell mit Modell, sondern **Mode
## A.7 Lösungen zu Kapitel „Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung“ {#sec:loesungen-cpsat}
**6.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.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.
**6.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.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).
**6.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.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.
**6.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.
**6.5 — Sudoku.**
**7.5 — Sudoku.**
```python
x = [[m.NewIntVar(1, 9, f"x{i}{j}") for j in range(9)] for i in range(9)]
for i in range(9): m.AddAllDifferent(x[i]) # Zeilen
@ -24623,11 +24624,11 @@ for i in range(9):
```
**Etwa 10 Zeilen Modellcode** — und der Solver löst jedes Sudoku in Millisekunden. Das ist die Stärke globaler Constraints.
**6.6 — 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.6 — 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.
**6.7 — 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`).
**7.7 — 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 6.1 — Die Betriebsvereinbarung, die niemanden interessiert
### Finde den Denkfehler — Die Betriebsvereinbarung, die niemanden interessiert
(a) **Warum die Regel verletzt wird.** Weil sie einen **Preis** hat. Eine weiche Nebenbedingung verbietet nichts — sie verteuert. Der Solver vergleicht in jedem Schritt, was ihn eine Verletzung kostet und was sie ihm einbringt. Bei einem Gewicht von 1 gegen 10 lohnt sich eine Drei-Tage-Serie, sobald sie auch nur ein Zehntel eines Wunsches rettet. Der Solver hat genau das getan, was im Modell steht — er hat nur nicht getan, was gemeint war.
@ -24681,11 +24682,11 @@ 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}
**7.1 — Flusserhaltung.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
**8.1 — Flusserhaltung.** $b_i = (15+3) - (12+8) = -2$ → **Senke** (Nettobedarf 2).
**7.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.
**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.
**7.3 — Transportproblem.** (a) Angebot $30+25+45 = 100$, Bedarf $25+30+20+25 = 100$ ✓ (b) Optimale Kosten: **790**. Transportplan:
**8.3 — Transportproblem.** (a) Angebot $30+25+45 = 100$, Bedarf $25+30+20+25 = 100$ ✓ (b) Optimale Kosten: **790**. Transportplan:
| von \ nach | L1 | L2 | L3 | L4 | Summe |
| --- | ---: | ---: | ---: | ---: | ---: |
@ -24696,15 +24697,15 @@ Das ist eine Frage, die eine Zahl als Antwort hat. Und wenn der Auftraggeber sie
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.
**7.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.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.
**7.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.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).
**7.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.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.
**7.7 — TSP mit MTZ.** Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 1215 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.7 — TSP mit MTZ.** Erwartetes Ergebnis: Bei 8 Städten löst das MILP in Sekunden. Ab etwa 1215 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 7.1 — Die vergessene Dimension
### Finde den Denkfehler — Die vergessene Dimension
(a) **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.
@ -24735,7 +24736,7 @@ ladung = loesung.Value(routing.GetDimensionOrDie("Ladung").CumulVar(index))
Diese Zeile fragt das Modell, ob das Modell sich an sich selbst hält. Existiert die Dimension „Ladung“ gar nicht, stürzt sie ab — und existiert sie, kann sie per Konstruktion nie verletzt sein. Sie prüft also entweder nichts oder gar nichts.
**Die allgemeine Regel:** Eine Prüfung, die dieselben Bausteine verwendet wie das Modell, prüft das Modell gegen sich selbst. Nützlich ist nur eine Prüfung, die von der **ausgegebenen Lösung** ausgeht und die Anforderungen der Wirklichkeit unabhängig nachrechnet — genau wie die Kostengegenrechnung in [Denkfehler 5.1](#kapitel-5) und die Verbrauchsprüfung in [Denkfehler 1.1](#kapitel-1).
**Die allgemeine Regel:** Eine Prüfung, die dieselben Bausteine verwendet wie das Modell, prüft das Modell gegen sich selbst. Nützlich ist nur eine Prüfung, die von der **ausgegebenen Lösung** ausgeht und die Anforderungen der Wirklichkeit unabhängig nachrechnet — genau wie die Kostengegenrechnung in [Abschnitt 6.10](#sec:milp-denkfehler) und die Verbrauchsprüfung in [Abschnitt 1.10](#sec:einfuehrung-denkfehler).
### Micro-Quiz {#quiz-loesung-graphen}
@ -24743,7 +24744,7 @@ Diese Zeile fragt das Modell, ob das Modell sich an sich selbst hält. Existiert
**2 — (b) die Struktur geht verloren.** „Höchstens 4 von 12“ ist eine Kardinalitätsregel; ihre Zeile passt nicht in das Schema, das die totale Unimodularität sichert. Die Relaxation kann dann Brüche liefern (etwa vier Monteure zu je 0,75 Überstunden), und man braucht Binärvariablen und Branch-and-Bound. (a) ist die gefährliche Fehlannahme: Die Eigenschaft ist **zerbrechlich**, eine einzige zusätzliche Zeile genügt. (c) verwechselt „schwerer zu lösen“ mit „unlösbar“.
**3 — (b) fehlende Dimension.** Ohne begrenzende Dimension ist jede zusätzliche Tour reine Zusatzstrecke — ein Optimierer legt dann folgerichtig alles auf ein Fahrzeug. Genau dieses Bild zeigt Denkfehler 7.1. (a) wäre zu prüfen, wenn die Lösung *schlecht* aussähe, nicht wenn sie verdächtig gut ist. (c) ist zwar eine sinnvolle Datenprüfung, erklärt aber nicht, warum drei Fahrzeuge unbenutzt bleiben.
**3 — (b) fehlende Dimension.** Ohne begrenzende Dimension ist jede zusätzliche Tour reine Zusatzstrecke — ein Optimierer legt dann folgerichtig alles auf ein Fahrzeug. Genau dieses Bild zeigt [Abschnitt 8.7](#sec:graphen-denkfehler). (a) wäre zu prüfen, wenn die Lösung *schlecht* aussähe, nicht wenn sie verdächtig gut ist. (c) ist zwar eine sinnvolle Datenprüfung, erklärt aber nicht, warum drei Fahrzeuge unbenutzt bleiben.
### Selbsttest {#selbsttest-loesung-graphen}
1. Abfluss minus Zufluss = Knotensaldo; entspricht der Kirchhoffschen Knotenregel.
@ -24756,15 +24757,15 @@ Diese Zeile fragt das Modell, ob das Modell sich an sich selbst hält. Existiert
## A.9 Lösungen zu Kapitel „Metaheuristiken — wenn der exakte Solver aussteigt“ {#sec:loesungen-metaheuristiken}
**Die Kurzsichtigkeit von Hand.** Ab *Schwarz* wählt die Faustregel: Schwarz → Dunkelrot (26) → Rot (5) → … und muss dann in die helle Gruppe: Rot → Weiß (50) → Elfenbein (2) → Beige (2). Summe **85 Minuten** — also noch einen Deut schlechter als die 84 ab Weiß. Der Grund ist derselbe: Die Regel erwischt den Gruppenwechsel an der teuersten Stelle. Bemerkenswert ist aber, dass der *Startpunkt* kaum etwas ändert. Das ist typisch: Eine kurzsichtige Regel scheitert nicht am Anfang, sondern an der Reihenfolge, in der sie ihre Möglichkeiten verbraucht.
**9.1 — Die Kurzsichtigkeit von Hand.** Ab *Schwarz* wählt die Faustregel: Schwarz → Dunkelrot (26) → Rot (5) → … und muss dann in die helle Gruppe: Rot → Weiß (50) → Elfenbein (2) → Beige (2). Summe **85 Minuten** — also noch einen Deut schlechter als die 84 ab Weiß. Der Grund ist derselbe: Die Regel erwischt den Gruppenwechsel an der teuersten Stelle. Bemerkenswert ist aber, dass der *Startpunkt* kaum etwas ändert. Das ist typisch: Eine kurzsichtige Regel scheitert nicht am Anfang, sondern an der Reihenfolge, in der sie ihre Möglichkeiten verbraucht.
**Zuggröße und Temperatur.** Gesucht ist $T$ mit $e^{-70/T} = e^{-1}$, also **$T = 70$**. Die getesteten Temperaturen liegen zwischen 0,5 und 8 — allesamt **eine Größenordnung darunter**. Bei $T_0 = 8$ hat eine mediane Verschlechterung die Annahmewahrscheinlichkeit $e^{-70/8} \approx 0{,}00016$. Genau das ist der Punkt der Warnung im Kapitel: Die verbreitete Regel „20 bis 50 % Annahmequote“ würde hier $T_0 \approx 50$ verlangen, und bei dieser Temperatur ist die Suche ein reiner Zufallslauf.
**9.2 — Zuggröße und Temperatur.** Gesucht ist $T$ mit $e^{-70/T} = e^{-1}$, also **$T = 70$**. Die getesteten Temperaturen liegen zwischen 0,5 und 8 — allesamt **eine Größenordnung darunter**. Bei $T_0 = 8$ hat eine mediane Verschlechterung die Annahmewahrscheinlichkeit $e^{-70/8} \approx 0{,}00016$. Genau das ist der Punkt der Warnung im Kapitel: Die verbreitete Regel „20 bis 50 % Annahmequote“ würde hier $T_0 \approx 50$ verlangen, und bei dieser Temperatur ist die Suche ein reiner Zufallslauf.
**Die teure Bewertung.** Statt `delta_verschieben` die Reihenfolge tatsächlich umbauen und `gesamtruestzeit()` neu rechnen. Bei $n = 200$ kostet das rund 200 Matrixzugriffe statt sechs. Erwartung: Die Zugzahl je Sekunde bricht um etwa zwei Größenordnungen ein. Bei gleichem **Zeitbudget** kommt die Suche entsprechend weniger weit; bei gleichem **Zugbudget** ist das Ergebnis identisch (die Bewertung ist ja korrekt, nur langsam). Genau diese Unterscheidung ist der Lehrpunkt: Die Implementierung ändert nicht das Verfahren, sondern nur, wie viel davon in das Zeitbudget passt.
**9.3 — Die teure Bewertung.** Statt `delta_verschieben` die Reihenfolge tatsächlich umbauen und `gesamtruestzeit()` neu rechnen. Bei $n = 200$ kostet das rund 200 Matrixzugriffe statt sechs. Erwartung: Die Zugzahl je Sekunde bricht um etwa zwei Größenordnungen ein. Bei gleichem **Zeitbudget** kommt die Suche entsprechend weniger weit; bei gleichem **Zugbudget** ist das Ergebnis identisch (die Bewertung ist ja korrekt, nur langsam). Genau diese Unterscheidung ist der Lehrpunkt: Die Implementierung ändert nicht das Verfahren, sondern nur, wie viel davon in das Zeitbudget passt.
**Der Umschlagpunkt genauer.** Sinnvolle Zwischengrößen sind 250, 300, 350 und 400. Zu erwarten ist ein **verwaschener** Übergang, kein scharfer Punkt: Der exakte Solver verschlechtert sich nicht sprunghaft, sondern sein Gap wächst, und irgendwann liegt seine beste gefundene Lösung über der heuristischen. Wo genau das passiert, hängt zusätzlich vom Zeitlimit ab — mit 300 Sekunden statt 30 verschiebt sich der Punkt nach oben. Die belastbare Aussage ist deshalb nie „ab $n = 380$“, sondern immer „ab $n \approx 380$ **bei diesem Zeitbudget**“.
**9.4 — Der Umschlagpunkt genauer.** Sinnvolle Zwischengrößen sind 250, 300, 350 und 400. Zu erwarten ist ein **verwaschener** Übergang, kein scharfer Punkt: Der exakte Solver verschlechtert sich nicht sprunghaft, sondern sein Gap wächst, und irgendwann liegt seine beste gefundene Lösung über der heuristischen. Wo genau das passiert, hängt zusätzlich vom Zeitlimit ab — mit 300 Sekunden statt 30 verschiebt sich der Punkt nach oben. Die belastbare Aussage ist deshalb nie „ab $n = 380$“, sondern immer „ab $n \approx 380$ **bei diesem Zeitbudget**“.
**Zerstören nach Maß.** Die kostenbasierte Zerstörung entfernt gezielt dort, wo etwas zu holen ist, und findet deshalb pro Runde mehr. Sie hat aber zwei Nachteile: Sie muss die Übergangskosten jedes Mal sortieren, und die entfernten Aufträge liegen über den ganzen Plan verstreut — das Teilproblem für CP-SAT ist damit schwieriger als ein zusammenhängendes Fenster gleicher Größe, weil auch alle Einfügestellen offen sind.
**9.5 — Zerstören nach Maß.** Die kostenbasierte Zerstörung entfernt gezielt dort, wo etwas zu holen ist, und findet deshalb pro Runde mehr. Sie hat aber zwei Nachteile: Sie muss die Übergangskosten jedes Mal sortieren, und die entfernten Aufträge liegen über den ganzen Plan verstreut — das Teilproblem für CP-SAT ist damit schwieriger als ein zusammenhängendes Fenster gleicher Größe, weil auch alle Einfügestellen offen sind.
Die Aufgabe ist bewusst so gestellt, dass die Antwort **gemessen** werden muss. Wer nur die Ergebniswerte vergleicht, kann Strategie und Rundenzahl nicht trennen. Die saubere Auswertung protokolliert beides: Verbesserungen **je Runde** (Güte der Strategie) und Runden **je Sekunde** (ihr Preis).
@ -24800,28 +24801,28 @@ Die Konsequenz ist keine Absage an das Projekt — 6,2 % sind echt und werden ve
## A.10 Lösungen zu Kapitel „Spaltengenerierung“ {#sec:loesungen-dekomposition}
**Das Abbruchkriterium.** Die Eins ist der Zielfunktionskoeffizient eines Musters: Jedes geschnittene Muster verbraucht **genau eine** Mutterrolle, und die Zielfunktion lautet $\min \sum_p x_p$. Die reduzierten Kosten einer neuen Spalte sind $c_p - \pi^\top a_p = 1 - \sum_i \pi_i a_{ip}$; sie sind negativ — die Spalte lohnt sich also —, wenn $\sum_i \pi_i a_{ip} > 1$.
**10.1 — Das Abbruchkriterium.** Die Eins ist der Zielfunktionskoeffizient eines Musters: Jedes geschnittene Muster verbraucht **genau eine** Mutterrolle, und die Zielfunktion lautet $\min \sum_p x_p$. Die reduzierten Kosten einer neuen Spalte sind $c_p - \pi^\top a_p = 1 - \sum_i \pi_i a_{ip}$; sie sind negativ — die Spalte lohnt sich also —, wenn $\sum_i \pi_i a_{ip} > 1$.
Wäre die Zielfunktion eine andere — etwa „minimiere den Verschnitt in Millimetern" —, stünde dort statt der Eins der Verschnitt des Musters. Die Eins ist also kein Zauberwert, sondern schlicht die Kosten der Spalte.
**Die Startbasis.** Ein Muster, das nur eine einzige Breite enthält, ist immer zulässig: $\lfloor W / b_i \rfloor$ Stücke der Breite $i$ passen in jede Rolle. Mit diesen $n$ Mustern lässt sich **jeder** Bedarf decken — notfalls sehr verschwenderisch, aber zulässig.
**10.2 — Die Startbasis.** Ein Muster, das nur eine einzige Breite enthält, ist immer zulässig: $\lfloor W / b_i \rfloor$ Stücke der Breite $i$ passen in jede Rolle. Mit diesen $n$ Mustern lässt sich **jeder** Bedarf decken — notfalls sehr verschwenderisch, aber zulässig.
Eine leere Startmenge wäre ein Problem, weil das Master-LP dann unzulässig ist: Es gibt keine Spalten, mit denen sich die Bedarfszeilen erfüllen ließen. Ohne zulässige Lösung gibt es keine Dualwerte, und ohne Dualwerte kann das Pricing nicht arbeiten. Die Schleife käme nie in Gang.
(In der Literatur behilft man sich alternativ mit künstlichen Variablen und sehr hohen Strafkosten — dieselbe Idee wie die Big-M-Phase des Simplex aus [Kapitel 5](#kap-lp).)
**Die Kennzahl prüfen.** Zu erwarten ist ein deutlicher, monotoner Zusammenhang: Je mehr Stücke auf eine Rolle passen, desto kleiner die Ersparnis. Bei zwei Stücken je Rolle entscheidet jede Paarung, und die Faustregel verschenkt regelmäßig ganze Rollen; ab acht bis zehn Stücken ist sie praktisch immer optimal.
**10.3 — Die Kennzahl prüfen.** Zu erwarten ist ein deutlicher, monotoner Zusammenhang: Je mehr Stücke auf eine Rolle passen, desto kleiner die Ersparnis. Bei zwei Stücken je Rolle entscheidet jede Paarung, und die Faustregel verschenkt regelmäßig ganze Rollen; ab acht bis zehn Stücken ist sie praktisch immer optimal.
Der Grund ist ein Verhältnis: Der Verschnitt einer Rolle ist höchstens so groß wie das kleinste nicht mehr passende Stück. Sind die Stücke klein gegen die Rolle, ist auch dieser Rest klein — und der Gesamtverschnitt nähert sich der theoretischen Untergrenze $\lceil \sum_i b_i d_i / W \rceil$, die jede Methode erreichen muss.
**Dienstplanung.** Eine „Spalte" ist ein **vollständiger zulässiger Wochenplan einer Person**: an welchen Tagen sie welche Schicht übernimmt.
**10.4 — Dienstplanung.** Eine „Spalte" ist ein **vollständiger zulässiger Wochenplan einer Person**: an welchen Tagen sie welche Schicht übernimmt.
* **Ins Master gehört**, was Personen miteinander koppelt: Jede Schicht muss besetzt sein ($\sum_p a_{sp} x_p \ge$ Bedarf je Schicht $s$), und jede Person bekommt genau einen Plan.
* **Ins Teilproblem gehört**, was innerhalb einer Person gilt: Höchstarbeitszeit, Ruhezeiten, Qualifikation, keine zwei Schichten am selben Tag, höchstens zwei Wochenenden im Monat.
Das ist der eigentliche Gewinn der Zerlegung: Die komplizierten arbeitsrechtlichen Regeln stehen im Teilproblem und werden dort **einmal je Person** geprüft, statt das Master zu überfrachten. Das Teilproblem ist meist ein kürzester Weg über ein Zeit-Zustands-Netz ([Kapitel 8](#kap-graphen)) statt eines Rucksacks.
**Branch-and-Price.** Das Problem: Verzweigt man auf einer Mustervariablen ($x_p \le 3$ gegen $x_p \ge 4$), so ist diese Bedingung im **Pricing** nicht darstellbar. Das Teilproblem erzeugt Muster, es kennt keine Verzweigungsentscheidungen über einzelne Spalten — es könnte im nächsten Schritt genau das ausgeschlossene Muster wieder erzeugen.
**10.5 — Branch-and-Price.** Das Problem: Verzweigt man auf einer Mustervariablen ($x_p \le 3$ gegen $x_p \ge 4$), so ist diese Bedingung im **Pricing** nicht darstellbar. Das Teilproblem erzeugt Muster, es kennt keine Verzweigungsentscheidungen über einzelne Spalten — es könnte im nächsten Schritt genau das ausgeschlossene Muster wieder erzeugen.
Branch-and-Price löst das, indem es auf **Originalgrößen** verzweigt statt auf Spalten: etwa „in wie vielen Rollen kommen Breite $i$ und Breite $j$ gemeinsam vor?" Solche Bedingungen lassen sich ins Pricing-Teilproblem einbauen, weil sie über die Struktur eines Musters sprechen und nicht über seine Identität.
@ -24867,19 +24868,19 @@ Die Schranke sagt: *Weniger als 73 Rollen sind unmöglich.* Sie sagt **nicht**:
## A.11 Lösungen zu Kapitel „Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität“ {#sec:loesungen-qp-nlp}
**8.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.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.
**8.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**.
**8.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 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$ ✓.
**8.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.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$.
**8.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.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.
**8.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).
**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).
### Finde den Denkfehler 8.1 — Die Kovarianzmatrix aus dem Controlling
### Finde den Denkfehler — Die Kovarianzmatrix aus dem Controlling
(a) **Ohne zu rechnen.** Stellen Sie sich die drei Anlagen als drei Personen vor, die nebeneinander gehen. A und B gehen fast im Gleichschritt ($0{,}9$). B und C gehen fast im Gleichschritt ($0{,}9$). Können A und C dann in *entgegengesetzte* Richtungen laufen? Nein — wenn A B folgt und B C folgt, muss A ungefähr auch C folgen. Korrelation ist zwar nicht transitiv im strengen Sinne, aber sie ist **eingeschränkt**: Zwei starke Bindungen zwingen die dritte in einen engen Bereich.
@ -24918,7 +24919,7 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R
### Micro-Quiz {#quiz-loesung-qp-nlp}
**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 Denkfehler 8.1 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.
**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 eingehalten.
@ -24935,26 +24936,26 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R
## A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“ {#sec:loesungen-unsicherheit}
**9.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.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.
**9.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).
**9.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.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.
**9.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.“
**9.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.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.
**9.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.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.“
**9.7 — Budgeted Uncertainty.**
**12.7 — Budgeted Uncertainty.**
```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))
```
$\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}$.
### Finde den Denkfehler 9.1 — Warum jedes Projekt zu spät fertig wird
### Finde den Denkfehler — Warum jedes Projekt zu spät fertig wird
(a) **Warum 14,38 statt 11,34 Tage.** Der Projektleiter hat den Mittelwert der **Einzeldauer** berechnet und ihn für den Mittelwert der **Projektdauer** gehalten. Das sind zwei verschiedene Größen:
@ -25003,7 +25004,7 @@ Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauer
**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 Denkfehler 9.1 (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.
**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.
### Selbsttest {#selbsttest-loesung-unsicherheit}
1. Wegen der Jensenschen Ungleichung: Bei konvexen Kosten ist $\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])$ — Planung mit dem Mittelwert unterschätzt die Kosten systematisch.
@ -25018,13 +25019,13 @@ Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauer
## A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“ {#sec:loesungen-dynamische-programmierung}
**10.1 — Bausteine.** Stufe = Tag 15; 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.1 — Bausteine.** Stufe = Tag 15; 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).
**10.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.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).
**10.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.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$.
**10.4 — Rucksack als DP.**
**13.4 — Rucksack als DP.**
```python
V = np.zeros((n + 1, kapazitaet + 1))
for i in range(1, n + 1):
@ -25035,11 +25036,11 @@ for i in range(1, n + 1):
```
Ergebnis identisch zum MILP (110). Laufzeit $O(n \cdot C)$ — bei kleinen ganzzahligen Kapazitäten schneller als Branch-and-Bound, bei großen oder nicht-ganzzahligen Kapazitäten schlechter (dann ist das MILP überlegen). Man nennt das **pseudopolynomiell**.
**10.5 — Risikoaversion kalibrieren.** (a) Zwischen $\lambda = 10^{-5}$ (41 %) und $10^{-4}$ (78 %); genauer etwa bei $\lambda \approx 3\cdot10^{-5}$. (b) Höheres $\lambda$ → höhere Gesamtkosten (man kauft Sicherheit mit Slippage). (c) Iterativ: $\lambda$ so wählen, dass der Anteil der Marktauswirkungskosten an den Gesamtkosten bei 20 % liegt — im Programm ausrechnen und per Bisektion einstellen.
**13.5 — Risikoaversion kalibrieren.** (a) Zwischen $\lambda = 10^{-5}$ (41 %) und $10^{-4}$ (78 %); genauer etwa bei $\lambda \approx 3\cdot10^{-5}$. (b) Höheres $\lambda$ → höhere Gesamtkosten (man kauft Sicherheit mit Slippage). (c) Iterativ: $\lambda$ so wählen, dass der Anteil der Marktauswirkungskosten an den Gesamtkosten bei 20 % liegt — im Programm ausrechnen und per Bisektion einstellen.
**10.6 — Zustandsraum erweitern.** Der Zustand wird zu (Restbestand, Orderbuchtiefe); der Zustandsraum verdreifacht sich, die Rechenzeit ebenfalls. Die Strategie wird **zustandsabhängig**: Bei tiefem Buch wird mehr verkauft, bei dünnem gewartet. Genau diese Adaptivität ist der Mehrwert von DP gegenüber einem festen Pfad.
**13.6 — Zustandsraum erweitern.** Der Zustand wird zu (Restbestand, Orderbuchtiefe); der Zustandsraum verdreifacht sich, die Rechenzeit ebenfalls. Die Strategie wird **zustandsabhängig**: Bei tiefem Buch wird mehr verkauft, bei dünnem gewartet. Genau diese Adaptivität ist der Mehrwert von DP gegenüber einem festen Pfad.
### Finde den Denkfehler 10.1 — Der Zustand, der zu wenig weiß
### Finde den Denkfehler — Der Zustand, der zu wenig weiß
(a) **Die fehlende Information.** Die Zeile
@ -25087,7 +25088,7 @@ Eine Prüfung, die nur den ausgegebenen Plan nachrechnet, bestätigt also alles.
**2 — (b).** Das Optimalitätsprinzip sagt, dass jedes **Reststück** einer optimalen Lösung selbst optimal ist. Deshalb genügt es, je Zustand einen einzigen Wert zu speichern, statt alle Wege dorthin zu unterscheiden. (a) ist die verbreitetste Fehldeutung — sie beschriebe gieriges Vorgehen, und genau das scheitert im Schnellstart dieses Kapitels (21 statt 14 Minuten). (c) ist eine Allgemeinplatz-Aussage ohne Bezug zum Prinzip.
**3 — (b).** Der Rabatt hängt von der bisher kumulierten Menge ab; ohne diese Größe im Zustand kann das Modell die Kosten der nächsten Bestellung nicht bestimmen — dasselbe Muster wie in Denkfehler 10.1. (a) übersieht genau das: Kosten, die von der Vorgeschichte abhängen, **sind** Zustandsinformation. (c) ist zu absolut — DP bleibt anwendbar, der Zustandsraum wird nur größer; ob sich stattdessen ein MILP lohnt, ist eine Frage der Größe, nicht der Eignung.
**3 — (b).** Der Rabatt hängt von der bisher kumulierten Menge ab; ohne diese Größe im Zustand kann das Modell die Kosten der nächsten Bestellung nicht bestimmen — dasselbe Muster wie in [Abschnitt 13.7](#sec:dynamische-programmierung-denkfehler). (a) übersieht genau das: Kosten, die von der Vorgeschichte abhängen, **sind** Zustandsinformation. (c) ist zu absolut — DP bleibt anwendbar, der Zustandsraum wird nur größer; ob sich stattdessen ein MILP lohnt, ist eine Frage der Größe, nicht der Eignung.
### Selbsttest {#selbsttest-loesung-dynamische-programmierung}
1. Jeder Teilabschnitt einer optimalen Strategie ist selbst optimal für den Zustand, in dem er beginnt.
@ -25100,25 +25101,25 @@ Eine Prüfung, die nur den ausgegebenen Plan nachrechnet, bestätigt also alles.
## A.14 Lösungen zu Kapitel „Mehrere Ziele — Pareto-Fronten statt Gewichte“ {#sec:loesungen-mehrziel}
**Dominanz prüfen.** P (14 000 €, 6 000 kg) und Q (13 800 €, 6 100 kg): **keiner dominiert den anderen.** Q ist billiger, P ist sauberer — beide sind pareto-optimal, die Wahl ist eine Präferenzfrage.
**14.1 — Dominanz prüfen.** P (14 000 €, 6 000 kg) und Q (13 800 €, 6 100 kg): **keiner dominiert den anderen.** Q ist billiger, P ist sauberer — beide sind pareto-optimal, die Wahl ist eine Präferenzfrage.
R (14 000 €, 6 100 kg) dagegen wird **von beiden dominiert**: gleich teuer wie P, aber schmutziger; schmutziger als Q *und* teurer. R kann man wegwerfen, ohne jemanden zu fragen — und genau das ist der Zweck des Dominanzbegriffs: Er trennt die Fälle, in denen der Modellierer entscheiden darf, von denen, in denen er es nicht darf.
**Der Schattenpreis in der Praxis.** Man geht die Front von unten durch, solange der Schattenpreis unter 0,90 €/kg liegt. Aus der Ausgabe: 0,58 — 0,93 — 0,40 — 0,59 — 0,77 — 0,65 — 0,77 — 1,09 — 1,32. Die Werte steigen nicht monoton, weil die Front bei ganzzahligen Modellen keine glatte Kurve ist; maßgeblich ist deshalb nicht der einzelne Schritt, sondern der **Gesamtschattenpreis** gegenüber dem Kostenminimum.
**14.2 — Der Schattenpreis in der Praxis.** Man geht die Front von unten durch, solange der Schattenpreis unter 0,90 €/kg liegt. Aus der Ausgabe: 0,58 — 0,93 — 0,40 — 0,59 — 0,77 — 0,65 — 0,77 — 1,09 — 1,32. Die Werte steigen nicht monoton, weil die Front bei ganzzahligen Modellen keine glatte Kurve ist; maßgeblich ist deshalb nicht der einzelne Schritt, sondern der **Gesamtschattenpreis** gegenüber dem Kostenminimum.
Der letzte Punkt, dessen Gesamtschattenpreis unter 0,90 € liegt, ist **14 189 € / 5 632 kg**: 646 € Aufpreis für 843 kg Ersparnis, also 0,77 €/kg. Der nächste Punkt kostet 1,09 €/kg und liegt damit über dem internen Preis.
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.“*
**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.
**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.
**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.
**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})$.
**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.
@ -25158,23 +25159,23 @@ Deshalb genügt es nicht, sorgfältig zu sein — man braucht ein Verfahren mit
## A.15 Lösungen zu Kapitel „Predict-then-Optimize“ {#sec:loesungen-prognose}
**Andere Preise.** Der Überhang kostet nur noch 1,50 €, die Fehlmenge weiterhin 6 €. Das kritische Verhältnis steigt von $6/9 = 0{,}667$ auf $6/7{,}5 = 0{,}80$. Die Bestellmenge verschiebt sich also **nach oben** — man bestellt jetzt das 80-%-Quantil statt des 66,7-%-Quantils.
**15.1 — Andere Preise.** Der Überhang kostet nur noch 1,50 €, die Fehlmenge weiterhin 6 €. Das kritische Verhältnis steigt von $6/9 = 0{,}667$ auf $6/7{,}5 = 0{,}80$. Die Bestellmenge verschiebt sich also **nach oben** — man bestellt jetzt das 80-%-Quantil statt des 66,7-%-Quantils.
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.
**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.
**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.
**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.)
**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.
**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".
**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.
@ -25215,27 +25216,27 @@ Es ist also nicht die bessere Prognose, die schadet, sondern die **nicht mitgezo
## A.16 Lösungen zu Kapitel „Die Strukturbrücke — dieselbe Mathematik, zwei Welten“ {#sec:loesungen-bruecke}
**Die dritte Ressource.** „Liquidität" — wie schnell sich eine Position ohne Kursabschlag verkaufen lässt — entspricht in der Werkstatt der **Umrüstbarkeit** oder der **Vorlaufzeit**: Wie schnell lässt sich die Fertigung von diesem Produkt wieder wegdrehen, wenn der Auftrag storniert wird? In beiden Fällen ist es eine knappe Größe, die nichts mit dem Ertrag zu tun hat und trotzdem die Auswahl einschränkt.
**16.1 — Die dritte Ressource.** „Liquidität" — wie schnell sich eine Position ohne Kursabschlag verkaufen lässt — entspricht in der Werkstatt der **Umrüstbarkeit** oder der **Vorlaufzeit**: Wie schnell lässt sich die Fertigung von diesem Produkt wieder wegdrehen, wenn der Auftrag storniert wird? In beiden Fällen ist es eine knappe Größe, die nichts mit dem Ertrag zu tun hat und trotzdem die Auswahl einschränkt.
Im Modell ist es schlicht eine weitere Zeile in `kapazitaeten` und ein weiterer Eintrag in jedem `verbrauch`. Genau das ist der Punkt des Kapitels: Eine neue Ressource kostet keinen Modellcode.
**Den Schattenpreis lesen.** Bis zu **602,60 €** für 10 Einheiten (10 × 60,26 €) lohnt sich der Kauf — darüber nicht.
**16.2 — Den Schattenpreis lesen.** Bis zu **602,60 €** für 10 Einheiten (10 × 60,26 €) lohnt sich der Kauf — darüber nicht.
Die Einschränkung aus [Abschnitt 5.9](#sec:lp-entartung): Der Schattenpreis gilt nur **lokal**, solange sich die optimale Basis nicht ändert. Nach einer gewissen Erhöhung wird eine andere Nebenbedingung bindend (hier voraussichtlich das Kapital), und ab dort ist die zusätzliche Einheit weniger wert. Bei 10 Einheiten auf 90 ist das eine Erhöhung um 11 % — schon groß genug, dass man es nachrechnen sollte, statt zu extrapolieren.
Zweite Einschränkung: Bei **Entartung** ist der Schattenpreis nicht eindeutig. Verschiedene Solver können dann verschiedene, gleichermaßen gültige Werte liefern — auch das steht in [Abschnitt 5.9](#sec:lp-entartung).
**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.
**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.
**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.
**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.
**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“ {#denkfehler-loesung-bruecke}
@ -25271,11 +25272,11 @@ Markowitz liefert stetige Anteile: 7,3 % bei Lieferant A, 4,1 % bei B. Ein Liefe
## A.17 Lösungen zu Kapitel „Supply-Chain und Energieeinsatz unter Unsicherheit“ {#sec:loesungen-supplychain}
**Der Umschlagpunkt.** Bei 60 MW statt 120 MW halbiert sich die Grenzkostendifferenz je Stunde (83 €/MWh × 60 MW = 4 980 €/h). Der Umschlagpunkt verdoppelt sich auf **7,7 Stunden**.
**17.1 — Der Umschlagpunkt.** Bei 60 MW statt 120 MW halbiert sich die Grenzkostendifferenz je Stunde (83 €/MWh × 60 MW = 4 980 €/h). Der Umschlagpunkt verdoppelt sich auf **7,7 Stunden**.
Das ist plausibel: Je kleiner die abzudeckende Leistung, desto länger dauert es, bis der Grenzkostenvorteil des großen Blocks seine Anfahrkosten einspielt. Ein Kernblock lohnt sich für eine kleine Restlast noch weniger als für eine große — was erklärt, warum Grundlastblöcke gerade dann unwirtschaftlich werden, wenn viel Wind einspeist und nur eine kleine Restlast bleibt.
**Die fehlende Nebenbedingung.** Analog zum Anfahren braucht man eine Abfahr-Indikatorvariable $b_{k,t} \ge u_{k,t-1} - u_{k,t}$ und dann:
**17.2 — Die fehlende Nebenbedingung.** Analog zum Anfahren braucht man eine Abfahr-Indikatorvariable $b_{k,t} \ge u_{k,t-1} - u_{k,t}$ und dann:
$$
u_{k,\tau} \le 1 - b_{k,t} \qquad \text{für } \tau = t, \dots, t + M_k - 1
@ -25285,17 +25286,17 @@ In Worten: Wer abfährt, bleibt $M_k$ Stunden aus.
Der reale Sachverhalt: Ein abgeschalteter Dampfblock kühlt aus und darf aus werkstofftechnischen Gründen nicht sofort wieder hochgefahren werden — die Temperaturwechsel würden das Material schädigen. Bei Gasturbinen ist die Mindeststillstandszeit kurz oder null, bei Kernblöcken beträgt sie viele Stunden.
**Wie viele Szenarien?** Zu erwarten ist, dass der Commitment-Plan ab einer gewissen Szenarienzahl stabil bleibt — oft schon bei 20 bis 40. Der Grund: Die erste Stufe ist binär und **grob**; sie kann nur ganze Blockstunden verschieben. Feine Unterschiede in der Szenarienmenge ändern daran nichts mehr.
**17.3 — Wie viele Szenarien?** Zu erwarten ist, dass der Commitment-Plan ab einer gewissen Szenarienzahl stabil bleibt — oft schon bei 20 bis 40. Der Grund: Die erste Stufe ist binär und **grob**; sie kann nur ganze Blockstunden verschieben. Feine Unterschiede in der Szenarienmenge ändern daran nichts mehr.
Der Zusammenhang mit [Abschnitt 15.6](#sec:prognose-messfalle) ist der interessante Teil: Dort ging es um die Zahl der **Beobachtungen** bei einer Bewertung, hier um die Zahl der **Szenarien** in einem Modell. Beide Male lautet die Frage nicht „wie viele sind genug?", sondern „ab wann ändert sich die Antwort nicht mehr?" — und beide Male beantwortet man sie, indem man es ausprobiert, statt eine Faustregel zu übernehmen.
**Der Wert eines Speichers.** Der Speicher braucht Variablen für Ladung, Entladung und Füllstand je Szenario und Stunde, mit der Bilanz $F_{t} = F_{t-1} + \eta\, L_t - E_t/\eta$ und Grenzen für Leistung und Kapazität. Er gehört in die **zweite** Stufe: Wann geladen wird, darf sich am Tag selbst entscheiden.
**17.4 — Der Wert eines Speichers.** Der Speicher braucht Variablen für Ladung, Entladung und Füllstand je Szenario und Stunde, mit der Bilanz $F_{t} = F_{t-1} + \eta\, L_t - E_t/\eta$ und Grenzen für Leistung und Kapazität. Er gehört in die **zweite** Stufe: Wann geladen wird, darf sich am Tag selbst entscheiden.
Zu erwarten ist ein deutlicher Rückgang der erwarteten Kosten, und zwar aus zwei Quellen: Er verschiebt Energie aus billigen in teure Stunden *und* er ersetzt Vorhaltung — ein Speicher ist in Sekunden verfügbar, ein Kernblock in acht Stunden. Der zweite Effekt ist der größere und wird bei einer Betrachtung ohne Szenarien komplett übersehen.
Der Wert je MWh Kapazität ergibt sich als Kostenersparnis geteilt durch 400 MWh. Er ist mit den Investitionskosten vergleichbar — dieselbe Rechnung wie der Schattenpreis in [Kapitel 5](#kap-lp), nur über einen ganzen Tag.
**Wenn es größer wird.** Mit 20 Blöcken und 168 Stunden hat die erste Stufe 3 360 Binärvariablen statt 120, und die Szenarienkopplung vervielfacht die kontinuierlichen Variablen. Zu erwarten ist, dass der MIP-Gap nach dem Zeitlimit nicht mehr auf null geht.
**17.5 — Wenn es größer wird.** Mit 20 Blöcken und 168 Stunden hat die erste Stufe 3 360 Binärvariablen statt 120, und die Szenarienkopplung vervielfacht die kontinuierlichen Variablen. Zu erwarten ist, dass der MIP-Gap nach dem Zeitlimit nicht mehr auf null geht.
Zwei Wege aus [Kapitel 9](#kap-metaheuristiken): **Erstens** — und in der Praxis meist ausreichend — den Gap akzeptieren; 1 bis 2 % sind bei Unit Commitment üblich und betriebswirtschaftlich belanglos. **Zweitens** LNS: Das Commitment eines Tages herausbrechen und exakt neu optimieren, während der Rest der Woche festbleibt. Das ist genau die Struktur aus `Large_Neighborhood_Search.py` — ein zusammenhängendes Fenster zerstören und mit dem exakten Solver reparieren.
@ -25339,19 +25340,19 @@ Die Reserve ist also nicht zu klein, sondern **von der falschen Art**. Was fehlt
## A.18 Lösungen zu Kapitel „Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage“ {#sec:loesungen-finanzdaten}
**11.1 — Renditeart.** (a) diskret. (b) logarithmisch. (c) logarithmisch (bzw. diskret kumuliert über Produkt). (d) logarithmisch (statistische Eigenschaften).
**18.1 — Renditeart.** (a) diskret. (b) logarithmisch. (c) logarithmisch (bzw. diskret kumuliert über Produkt). (d) logarithmisch (statistische Eigenschaften).
**11.2 — Mittelwertfalle.** (a) Arithmetisches Mittel: $(60-40+60-40)/4 = +10\,\%$. (b) $10\,000 \cdot 1{,}6 \cdot 0{,}6 \cdot 1{,}6 \cdot 0{,}6 = 10\,000 \cdot 0{,}9216 = \mathbf{9216\ €}$ — ein **Verlust**. (c) Geometrisches Mittel: $0{,}9216^{1/4} - 1 = -2{,}0\,\%$ p. a. Der Unterschied zwischen $+10\,\%$ und $-2\,\%$ ist der Grund, warum Fondswerbung gern arithmetische Mittel zeigt.
**18.2 — Mittelwertfalle.** (a) Arithmetisches Mittel: $(60-40+60-40)/4 = +10\,\%$. (b) $10\,000 \cdot 1{,}6 \cdot 0{,}6 \cdot 1{,}6 \cdot 0{,}6 = 10\,000 \cdot 0{,}9216 = \mathbf{9216\ €}$ — ein **Verlust**. (c) Geometrisches Mittel: $0{,}9216^{1/4} - 1 = -2{,}0\,\%$ p. a. Der Unterschied zwischen $+10\,\%$ und $-2\,\%$ ist der Grund, warum Fondswerbung gern arithmetische Mittel zeigt.
**11.3 — Konditionszahl.** Erwartetes Muster: Bei $T = 40 < N = 30$… (hier $T > N$, aber knapp): sehr große Konditionszahl, kleinster Eigenwert nahe null. Bei $T = 100$: deutlich besser. Bei $T = 1000$: stabil. Faustregel $T \ge 5N$ bis $10N$.
**18.3 — Konditionszahl.** Erwartetes Muster: Bei $T = 40 < N = 30$… (hier $T > N$, aber knapp): sehr große Konditionszahl, kleinster Eigenwert nahe null. Bei $T = 100$: deutlich besser. Bei $T = 1000$: stabil. Faustregel $T \ge 5N$ bis $10N$.
**11.4 — Spaltenreihenfolge.** `raw["Close"].columns` ist alphabetisch; `raw["Close"][tickers]` stellt die eigene Reihenfolge her. Existiert ein Ticker nicht, wirft `[tickers]` einen `KeyError`**das ist erwünscht**: lieber ein lauter Fehler als eine stille Verschiebung.
**18.4 — Spaltenreihenfolge.** `raw["Close"].columns` ist alphabetisch; `raw["Close"][tickers]` stellt die eigene Reihenfolge her. Existiert ein Ticker nicht, wirft `[tickers]` einen `KeyError`**das ist erwünscht**: lieber ein lauter Fehler als eine stille Verschiebung.
**11.5 — Beide Ziele vergleichen.** Erwartetes Ergebnis: Das Konstant-Korrelations-Ziel schneidet bei Aktien meist etwas besser ab, weil es die unterschiedlichen Einzelvolatilitäten erhält. Der Unterschied ist aber kleiner als der Unterschied zwischen „mit“ und „ohne“ Shrinkage — die Wahl des Ziels ist zweitrangig gegenüber der Entscheidung, überhaupt zu schrumpfen.
**18.5 — Beide Ziele vergleichen.** Erwartetes Ergebnis: Das Konstant-Korrelations-Ziel schneidet bei Aktien meist etwas besser ab, weil es die unterschiedlichen Einzelvolatilitäten erhält. Der Unterschied ist aber kleiner als der Unterschied zwischen „mit“ und „ohne“ Shrinkage — die Wahl des Ziels ist zweitrangig gegenüber der Entscheidung, überhaupt zu schrumpfen.
**11.6 — Error-Maximizer messen.** (a)/(b) Erwartetes Ergebnis: Die Gleichgewichtung schlägt die Stichproben-Optimierung bis etwa $T/N \approx 5$10; Ledoit-Wolf schlägt sie schon früher. (c) **Praktische Folgerung:** Bei knapper Datenlage ist 1/N ein ernstzunehmender Kandidat, und jede Optimierung muss sich daran messen lassen.
**18.6 — Error-Maximizer messen.** (a)/(b) Erwartetes Ergebnis: Die Gleichgewichtung schlägt die Stichproben-Optimierung bis etwa $T/N \approx 5$10; Ledoit-Wolf schlägt sie schon früher. (c) **Praktische Folgerung:** Bei knapper Datenlage ist 1/N ein ernstzunehmender Kandidat, und jede Optimierung muss sich daran messen lassen.
### Finde den Denkfehler 11.1 — Das risikofreie Portfolio
### Finde den Denkfehler — Das risikofreie Portfolio
(a) **Der Rang.** Eine aus $T$ Beobachtungen geschätzte Kovarianzmatrix hat höchstens den Rang $T-1$ — hier also **19** statt der nötigen 30. Geometrisch heißt das: Die 20 beobachteten Renditevektoren spannen nur einen 19-dimensionalen Unterraum des 30-dimensionalen Anlageraums auf. Es bleiben **11 Richtungen** übrig, über die die Daten schlicht **nichts** aussagen.
@ -25400,7 +25401,7 @@ Und immer: **außerhalb des Schätzzeitraums prüfen.** Ein Modell, das nur im e
## A.19 Lösungen zu Kapitel „Die moderne Portfoliotheorie nach Markowitz“ {#sec:loesungen-markowitz}
**12.1 — Diversifikationseffekt.**
**19.1 — Diversifikationseffekt.**
| $(w_1, w_2)$ | $\mu_p$ | $\sigma_p$ | Sharpe ($r_f=2\,\%$) |
| --- | ---: | ---: | ---: |
@ -25414,19 +25415,19 @@ Rechenweg für $(0{,}5;\ 0{,}5)$: $\sigma_p^2 = 0{,}25\cdot0{,}0625 + 0{,}25\cdo
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.
**12.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.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).
**12.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.
**12.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.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.
**12.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.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.
**12.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.
**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.
**12.7 — Out-of-Sample.** Typisches Ergebnis: Max-Sharpe schneidet in der zweiten Hälfte **schlechter** ab als in der ersten — es hat Schätzrauschen mitoptimiert. GMV ist stabiler (keine Renditeprognose nötig), 1/N oft überraschend gut. Mit Ledoit-Wolf verbessern sich GMV und Max-Sharpe spürbar. **Praxisfolgerung:** Renditeprognosen sind viel unzuverlässiger als Risikoschätzungen — Modelle, die ohne sie auskommen, sind robuster.
**19.7 — Out-of-Sample.** Typisches Ergebnis: Max-Sharpe schneidet in der zweiten Hälfte **schlechter** ab als in der ersten — es hat Schätzrauschen mitoptimiert. GMV ist stabiler (keine Renditeprognose nötig), 1/N oft überraschend gut. Mit Ledoit-Wolf verbessern sich GMV und Max-Sharpe spürbar. **Praxisfolgerung:** Renditeprognosen sind viel unzuverlässiger als Risikoschätzungen — Modelle, die ohne sie auskommen, sind robuster.
### Finde den Denkfehler 12.1 — Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio
### Finde den Denkfehler — Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio
(a) **Woher die Spanne kommt.** Aus reinem Rauschen. Der Mittelwert von 250 Beobachtungen einer Größe mit Standardabweichung $\sigma = 1{,}2\,\%$ hat selbst noch den **Standardfehler**
@ -25458,7 +25459,7 @@ Zusammen ergibt das das beobachtete Bild: 38 % Konzentration in einer Anlage, ob
1. **Renditeschätzung ganz vermeiden.** Das **Minimum-Varianz-Portfolio** minimiert nur $\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}$ und braucht überhaupt kein $\boldsymbol{\mu}$. Damit fällt die unzuverlässigste Eingangsgröße ersatzlos weg. Das ist das wirksamste Mittel, weil es das Problem nicht abmildert, sondern **beseitigt** — man kann eine Größe nicht falsch schätzen, die man nicht verwendet.
2. **Stark schrumpfen.** Wenn Renditen gebraucht werden, zieht man sie kräftig zum Gesamtmittel (James-Stein) oder zu einer Gleichgewichtsannahme (Black-Litterman) — deutlich stärker als bei Kovarianzen, aus den Gründen unter (c).
3. **Freiheit begrenzen.** Obergrenzen je Position (etwa 15 %), Sektorgrenzen, maximale Abweichung von einer Referenzgewichtung. Was der Optimierer nicht darf, kann er auch nicht auf Rauschen setzen — dasselbe Rezept wie in [Denkfehler 11.1](#kapitel-11).
3. **Freiheit begrenzen.** Obergrenzen je Position (etwa 15 %), Sektorgrenzen, maximale Abweichung von einer Referenzgewichtung. Was der Optimierer nicht darf, kann er auch nicht auf Rauschen setzen — dasselbe Rezept wie in [Abschnitt 18.8](#sec:finanzdaten-denkfehler).
**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.
@ -25481,19 +25482,19 @@ Zusammen ergibt das das beobachtete Bild: 38 % Konzentration in einer Anlage, ob
## A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“ {#sec:loesungen-cvar}
**13.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 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}$.
**13.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.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.
**13.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.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.)
**13.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.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.
**13.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.
**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.
**13.6 — Grenze statt Strafe.** (a) Mit engerer Grenze sinkt die erreichbare Rendite; bei $\tau = 0$ bleibt das Altportfolio. (b) **Vorteil der Grenze:** garantierte Obergrenze für die Handelsaktivität — wichtig, wenn Liquidität oder Compliance eine harte Schranke verlangen. **Nachteil:** Sie kann das Modell unlösbar machen und ignoriert, dass ein sehr lohnender Trade den Aufwand wert wäre. (c) Strafe bei ökonomischer Abwägung, Grenze bei regulatorischen oder Liquiditätsvorgaben.
**20.6 — Grenze statt Strafe.** (a) Mit engerer Grenze sinkt die erreichbare Rendite; bei $\tau = 0$ bleibt das Altportfolio. (b) **Vorteil der Grenze:** garantierte Obergrenze für die Handelsaktivität — wichtig, wenn Liquidität oder Compliance eine harte Schranke verlangen. **Nachteil:** Sie kann das Modell unlösbar machen und ignoriert, dass ein sehr lohnender Trade den Aufwand wert wäre. (c) Strafe bei ökonomischer Abwägung, Grenze bei regulatorischen oder Liquiditätsvorgaben.
### Finde den Denkfehler 13.1 — Das Risikobudget, das durch Diversifikation stieg
### Finde den Denkfehler — Das Risikobudget, das durch Diversifikation stieg
(a) **Die entscheidende Wahrscheinlichkeit.** Bei zwei unabhängigen Anleihen mit je 4 % Ausfallwahrscheinlichkeit fällt **mindestens eine** aus mit
@ -25546,19 +25547,19 @@ 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}
**14.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.
**14.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.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.
**14.3 — Rebalancing-Kalender.** Etwa 2530 % 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 2530 % 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.
**14.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.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.
**14.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.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.
**14.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 14.1 — Die Strategie mit dem hochsignifikanten Ergebnis
### 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?“*
@ -25612,19 +25613,19 @@ Ein zweiter Blick auf denselben Testzeitraum ist deshalb kein „nochmal prüfen
## A.22 Lösungen zu Kapitel „Praxisfallen und der Weg zum produktiven Einsatz“ {#sec:loesungen-praxisfallen}
**15.1 — Hart oder weich, revisited.** (a) hart: gesetzliche Ruhezeit, Qualifikationspflicht. (b) weich: individuelle Wunschtage, Vermeidung von Freistunden. (c) diskutabel: Höchstzahl Vertretungsstunden pro Tag (tariflich vs. Notfall), gleichmäßige Wochenendverteilung.
**22.1 — Hart oder weich, revisited.** (a) hart: gesetzliche Ruhezeit, Qualifikationspflicht. (b) weich: individuelle Wunschtage, Vermeidung von Freistunden. (c) diskutabel: Höchstzahl Vertretungsstunden pro Tag (tariflich vs. Notfall), gleichmäßige Wochenendverteilung.
**15.2 — Zeitlimit wählen.** (a) 13 s, Gap 510 % (Echtzeit schlägt Optimalität). (b) 15 min, Gap 12 %. (c) Stunden, Gap ~0 % (einmalige, folgenreiche Entscheidung). (d) 3060 s, Gap 1 % — die Datenunsicherheit ist ohnehin größer.
**22.2 — Zeitlimit wählen.** (a) 13 s, Gap 510 % (Echtzeit schlägt Optimalität). (b) 15 min, Gap 12 %. (c) Stunden, Gap ~0 % (einmalige, folgenreiche Entscheidung). (d) 3060 s, Gap 1 % — die Datenunsicherheit ist ohnehin größer.
**15.3 — Relaxation einbauen.** Muster wie in `Infeasibility_Diagnose.py`: Schlupfvariable je Mindestbesetzung, Strafe deutlich über allen weichen Zielen, aber endlich. Wichtig: **gestaffelte** Strafen, damit der Solver die *am wenigsten schmerzhafte* Verletzung wählt.
**22.3 — Relaxation einbauen.** Muster wie in `Infeasibility_Diagnose.py`: Schlupfvariable je Mindestbesetzung, Strafe deutlich über allen weichen Zielen, aber endlich. Wichtig: **gestaffelte** Strafen, damit der Solver die *am wenigsten schmerzhafte* Verletzung wählt.
**15.4 — Erklärbarkeit.** Report-Struktur: (1) Zielwert und Aufschlüsselung nach Bestandteilen; (2) je Entscheidung die bindenden Bedingungen; (3) Schattenpreise der knappsten Ressourcen mit Handlungsempfehlung.
**22.4 — Erklärbarkeit.** Report-Struktur: (1) Zielwert und Aufschlüsselung nach Bestandteilen; (2) je Entscheidung die bindenden Bedingungen; (3) Schattenpreise der knappsten Ressourcen mit Handlungsempfehlung.
**15.5 — Gap gegen Laufzeit.** Erwartetes Muster: Von 10 % auf 2 % kostet wenig Zeit; von 1 % auf 0 % kann die Laufzeit um Größenordnungen steigen, ohne dass sich der Zielwert nennenswert verbessert. Empfehlung: Gap dort ansetzen, wo die Kurve knickt — typischerweise 12 %.
**22.5 — Gap gegen Laufzeit.** Erwartetes Muster: Von 10 % auf 2 % kostet wenig Zeit; von 1 % auf 0 % kann die Laufzeit um Größenordnungen steigen, ohne dass sich der Zielwert nennenswert verbessert. Empfehlung: Gap dort ansetzen, wo die Kurve knickt — typischerweise 12 %.
**15.6 — Post-Mortem.** Bewertungskriterien: Wird zwischen **Symptom**, **Ursache** und **fehlender Prüfung** unterschieden? Ist die abgeleitete Regel allgemein genug, um beim nächsten Projekt zu helfen (z. B. „Nach jeder Vorzeichenumkehr eine numerische Gegenprobe“), aber konkret genug, um überprüfbar zu sein?
**22.6 — Post-Mortem.** Bewertungskriterien: Wird zwischen **Symptom**, **Ursache** und **fehlender Prüfung** unterschieden? Ist die abgeleitete Regel allgemein genug, um beim nächsten Projekt zu helfen (z. B. „Nach jeder Vorzeichenumkehr eine numerische Gegenprobe“), aber konkret genug, um überprüfbar zu sein?
### Finde den Denkfehler 15.1 — Das Modell, das seit einem Jahr nicht mehr optimiert
### Finde den Denkfehler — Das Modell, das seit einem Jahr nicht mehr optimiert
(a) **Warum die konstante Laufzeit das Symptom ist.** Ein Solver, der fertig wird, braucht so lange, wie das Problem eben dauert — und diese Zeit wächst mit den Daten. Eine Laufzeit, die über Jahre exakt bei 3,00 Sekunden liegt, kann deshalb nur eines bedeuten: Der Job wird nicht fertig, sondern **abgeschnitten**.
@ -25675,11 +25676,11 @@ Das ist das eigentliche Argument für die Überwachung: Nicht, dass ein Gap von
## A.23 Lösungen zu Kapitel „Testen, Messen, Ausliefern“ {#sec:loesungen-testing}
**Den Schnellstart reparieren.** Zwei Wege: (a) **Abrunden statt runden**`np.floor` statt `np.round`. Das ist immer zulässig, weil weniger produzieren nie eine Kapazität sprengt, kostet aber Deckungsbeitrag und ist im Allgemeinen **nicht** die optimale ganzzahlige Lösung. (b) **Ganzzahlig modellieren**`linprog(..., integrality=1)` bzw. `IntVar`. Das ist der richtige Weg.
**23.1 — Den Schnellstart reparieren.** Zwei Wege: (a) **Abrunden statt runden**`np.floor` statt `np.round`. Das ist immer zulässig, weil weniger produzieren nie eine Kapazität sprengt, kostet aber Deckungsbeitrag und ist im Allgemeinen **nicht** die optimale ganzzahlige Lösung. (b) **Ganzzahlig modellieren**`linprog(..., integrality=1)` bzw. `IntVar`. Das ist der richtige Weg.
Warum das ein eigenes Kapitel wert ist ([Kapitel 6](#kap-milp)): Runden ist nicht nur ungenau, sondern kann *beliebig* danebenliegen. Es gibt Instanzen, bei denen die gerundete LP-Lösung nicht nur suboptimal, sondern unzulässig ist — genau das zeigt der Schnellstart — und andere, bei denen zwischen gerundetem LP und echtem Optimum Welten liegen (`Runden_Gegenbeispiel.py`).
**Eine Invariante mehr.**
**23.2 — Eine Invariante mehr.**
```python
def test_kapazitaeten_verdoppeln(bauer):
@ -25693,7 +25694,7 @@ def test_kapazitaeten_verdoppeln(bauer):
Bei einem **LP** gilt das exakt: Der zulässige Bereich wird um den Faktor 2 gestreckt, und weil die Zielfunktion linear ist, skaliert das Optimum mit. Bei einem **MILP** gilt es nicht: Die Ganzzahligkeitsbedingung skaliert nicht mit. Aus 30,67 wird beim Verdoppeln 61,33 — und ob dazwischen eine bessere ganzzahlige Lösung liegt, hängt vom Einzelfall ab. Der Test wäre dort also falsch.
**Eine eigene Mutation.** Zwei lohnende Kandidaten:
**23.3 — Eine eigene Mutation.** Zwei lohnende Kandidaten:
```python
("Kapazitaetspruefung mit falschem Vergleich",
@ -25704,9 +25705,9 @@ Bei einem **LP** gilt das exakt: Der zulässige Bereich wird um den Faktor 2 ges
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.
**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.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.
**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 `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.