Stichwortregister: automatische Markierung aller Glossarbegriffe (988 statt 456 Marker)

Neues Skript erzeuge_stichwortregister_04.py:
- Sucht alle Glossarbegriffe (Anzeigename) in den Kapiteldateien
  und markiert die erste Fundstelle je Datei mit {idx:Indexmarke}
- Termliste aus glossar_eintraege_04.py + vorhandenen {idx:...}-Markern
- Idempotent: erkennt vorhandene Marker und überspringt sie
- Überspringt Codeblöcke, Inline-Code und Math
- --check und --bericht Modi
- 605 Marker eingefügt (2 Durchläufe: 597 + 8)

Zusätzlich:
- titeltexte_04.py _saeubern(): entfernt {idx:...}-Marker vor
  Titeltext-Vergleich (pruefe_titeltexte schlug sonst fehl)
This commit is contained in:
dschlueter 2026-09-10 00:07:01 +02:00
commit 99cb85907c
38 changed files with 1529 additions and 1214 deletions

View file

@ -19,7 +19,7 @@
## Von der mathematischen Modellierung zu praktischen intelligenten Lösungen für Betrieb, Technik und Finanzmärkte {.unlisted .unnumbered}
**Ein praxisorientiertes Kompendium für Entscheider** mit Constraint Programming, Vektor-
und Matrixmethoden sowie HiGHS, SciPy, Google OR-Tools und CVXPY.
und Matrixmethoden sowie HiGHS{idx:HiGHS}, SciPy, Google OR-Tools{idx:OR-Tools} und CVXPY.
Mit Python planen, optimieren und entscheiden: von **Personal, Schichten und Logistik** über **Energie, Ressourcen und Netzwerke** bis zur **Portfoliooptimierung an den Finanzmärkten**.
@ -83,8 +83,8 @@ sowie Mathematik auf Grundstudiumsniveau:
| Gebiet | Was Sie können sollten | Wo im Buch gebraucht |
| --- | --- | --- |
| **Lineare Algebra** | Vektoren, Matrizen, Matrix-Vektor-Produkt, Eigenwerte | ab {ref:kap:fundament}, zentral in {ref:kap:qp-nlp}, {ref:kap:finanzdaten}, {ref:kap:markowitz} |
| **Analysis** | Ableitung, Gradient, notwendige Bedingung erster Ordnung | {ref:kap:qp-nlp} |
| **Lineare Algebra** | Vektoren, Matrizen, Matrix-Vektor-Produkt{idx:Matrix-Vektor-Produkt}, Eigenwerte | ab {ref:kap:fundament}, zentral in {ref:kap:qp-nlp}, {ref:kap:finanzdaten}, {ref:kap:markowitz} |
| **Analysis** | Ableitung, Gradient{idx:Gradient}, notwendige Bedingung erster Ordnung | {ref:kap:qp-nlp} |
| **Statistik** | Erwartungswert, Varianz, Standardabweichung, Quantil | {ref:kap:unsicherheit}, 11–14 |
| **Python** | Listen, Dictionaries, Schleifen, Funktionen, Klassen, NumPy-Arrays | durchgängig |
@ -132,8 +132,8 @@ Eine Besonderheit dabei — die Import-Reihenfolge ist bewusst gewählt: `highsp
native Bibliothek zuerst. Der Grund: `ortools` und `highspy` bringen jeweils eine eigene
HiGHS-Kopie mit, die sich nicht im selben Python-Prozess verträgt — und `cvxpy` importiert
ein installiertes `highspy` bei der Solver-Erkennung selbst mit. Lädt deren HiGHS-Kopie
zuerst, bricht der Test beim CP-SAT-Funktionstest mit einem kryptischen
`ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY verzichtet
zuerst, bricht der Test beim CP-SAT{idx:CP-SAT}-Funktionstest mit einem kryptischen
`ImportError: undefined symbol` ab; so gewinnt die Kopie von `ortools`, und CVXPY{idx:CVXPY} verzichtet
lediglich auf sein HIGHS-Interface (für alle Buchprogramme folgenlos). Details und
Abhilfen: {ref:sec:oekosystem-ein-system-vier-programmieransaetze} und
{ref:anhang:fehlerdiagnose}.
@ -367,7 +367,7 @@ Suchen Sie sich einen aus. Sie können auch alle vier überspringen; nötig sind
* **Für die Sitzung gibt es einen Bericht, keine Tabelle.**
{ref:sec:praxisfallen-attribution} beziffert je Nebenbedingung, was sie den Plan kostet,
und formuliert daraus Sätze in Alltagssprache. Für die Frage des Betriebsrats — *„warum
ausgerechnet ich?“* — liefert {ref:kap:praxisfallen} den Constraint-Trace, der die
ausgerechnet ich?“* — liefert {ref:kap:praxisfallen} den Constraint-Trace{idx:Constraint-Trace}, der die
einzelne Zuweisung begründet.
**Ihr Weg.** Zuerst Pfad E (2–4 Stunden): {ref:sec:einfuehrung-historischer-kontext-und-evolution}
@ -394,14 +394,14 @@ deshalb jedes Mal, worauf eine Zahl beruht.
* **Der Engpass lässt sich benennen und bepreisen.**
{ref:sec:praxisfallen-attribution} rechnet für jede Kapazität aus, was die nächste Stunde
wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis der
wert ist — und wie weit dieser Wert trägt. Im Beispiel gilt der Schattenpreis{idx:Schattenpreis} der
Lackiererei exakt bis **+15 Stunden**; eine ganze Sonderschicht von 30 Stunden bringt
deshalb 60 € statt der rechnerischen 120 €. **Die zweite Hälfte wäre bezahlt und
wirkungslos.**
* **Rüstzeiten, Reihenfolgen, Taktzeiten sind der Kern, nicht die Ausnahme.**
{ref:kap:cpsat} modelliert Maschinenbelegung mit Intervallvariablen,
{ref:kap:metaheuristiken} zeigt am selben Fall das Gesamtbild: Faustregel 2 497 Minuten
Rüstzeit, CP-SAT allein 2 628, Simulated Annealing 2 343, Annealing plus LNS **2 289** —
Rüstzeit{idx:Rüstzeit}, CP-SAT allein 2 628, Simulated Annealing{idx:Simulated Annealing} 2 343, Annealing plus LNS **2 289** —
und die untere Schranke bei 1 768, damit sichtbar bleibt, wie viel Luft noch da ist.
* **Wenn der Solver zu lange braucht, gibt es Zahlen statt Ratschläge.**
{ref:sec:cpsat-parallele-suche} misst, was mehrere Suchstränge bringen (hier Faktor 12,3)
@ -422,23 +422,23 @@ nachverhandelt, gewinnt nichts und hält das Verfahren für nutzlos.
> Titel, null in fünf anderen. Und im nächsten Quartal sieht sie völlig anders aus.“*
* **Der Grund dafür steht in {ref:kap:finanzdaten}, nicht in {ref:kap:markowitz}.** Eine
aus kurzen Zeitreihen geschätzte Kovarianzmatrix ist schlecht konditioniert; der
aus kurzen Zeitreihen geschätzte Kovarianzmatrix{idx:Kovarianzmatrix} ist schlecht konditioniert; der
Optimierer setzt dann auf Schätzrauschen. Shrinkage nach Ledoit-Wolf ist das Gegenmittel;
ihre Wirkung wird an **Eigenwertspektrum und Konditionszahl** gemessen, nicht behauptet.
ihre Wirkung wird an **Eigenwertspektrum und Konditionszahl**{idx:Konditionszahl} gemessen, nicht behauptet.
* **Der VaR verschweigt genau das, worauf es ankommt.** In der Stichprobe aus
{ref:kap:cvar} sagt der VaR: „an 95 % der Tage höchstens **1,86 %** Verlust“. Der CVaR
sagt: „und wenn doch, dann im Mittel **2,99 %**“. Der schlechteste Tag der Stichprobe
liegt bei **−23,0 %** — am VaR ändert dieser eine Tag **nichts**, am CVaR sehr wohl.
Deshalb ist der CVaR die Größe, die man optimiert.
* **Transaktionskosten und Rebalancing sind Teil des Modells, nicht ein Abzug danach.**
{ref:kap:cvar} nimmt sie in die Zielfunktion, {ref:kap:handelsmaschine} setzt den
* **Transaktionskosten{idx:Transaktionskosten} und Rebalancing sind Teil des Modells, nicht ein Abzug danach.**
{ref:kap:cvar} nimmt sie in die Zielfunktion{idx:Zielfunktion}, {ref:kap:handelsmaschine} setzt den
Backtest daneben — samt der Fallen, die einen guten Backtest wertlos machen.
**Ihr Weg.** Pfad D, und zwar **mit** {ref:kap:finanzdaten}. Danach Projekt **P6**
(Portfolio-Rebalancer) oder **P7** (Risikoreport mit CVaR und Stresstests).
**Woran es scheitert.** An der Reihenfolge. Wer bei Markowitz einsteigt und die
Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler maximiert —
Datenkapitel überspringt, bekommt ein Modell, das seine eigenen Schätzfehler{idx:Schätzfehler} maximiert —
und merkt es erst im Echtgeldbetrieb.
### Für Studierende und Data Scientists — die Methodik {-}
@ -540,43 +540,43 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| --- | --- | --- |
| `Installationstest.py` | Installationsprüfung | Vorspann |
| `Bot_Allokation.py` | Erstes CP-SAT-Modell | {ref:kap:einfuehrung} |
| `Brute_Force_Vergleich.py` | Kombinatorische Explosion | {ref:kap:einfuehrung} |
| `Brute_Force_Vergleich.py` | Kombinatorische Explosion{idx:Kombinatorische Explosion} | {ref:kap:einfuehrung} |
| `Bausteine_Vorlage.py` | Vorlage für eigene Modelle | {ref:kap:einfuehrung} |
| `Excel_Bruecke.py` | Excel-Mappe lesen, lösen, zurückschreiben | {ref:kap:einfuehrung} |
| `Matrixform.py` | Matrixform und Zulässigkeit | {ref:kap:fundament} |
| `Matrixform.py` | Matrixform{idx:Matrixform} und Zulässigkeit | {ref:kap:fundament} |
| `Konvexitaet_Demo.py` | Sehnen-Test, lokale Optima | {ref:kap:fundament} |
| `Visualisierung_Loesungsraum.py` | Polyeder mit Ecken | {ref:kap:fundament} |
| `Visualisierung_Loesungsraum.py` | Polyeder{idx:Polyeder} mit Ecken | {ref:kap:fundament} |
| `Skalierung_Kondition.py` | Konditionszahl, Ruiz, Toleranzen | {ref:kap:fundament} |
| `Solver_Wahl.py` | Entscheidungshilfe | {ref:kap:oekosystem} |
| `Ein_System_Vier_Ansaetze.py` | Vier Bibliotheken | {ref:kap:oekosystem} |
| `Modellierungsschichten.py` | Pyomo und Linopy | {ref:kap:oekosystem} |
| `Modellierungsschichten.py` | Pyomo{idx:Pyomo} und Linopy{idx:Linopy} | {ref:kap:oekosystem} |
| `Vektorisierte_Modellgenerierung.py` | Aufbauzeit vs. Lösezeit | {ref:kap:oekosystem} |
| `Vom_Wunsch_zum_Modell.py` | Fünf Modelle auf denselben Daten | {ref:kap:modellierung} |
| `Simplex_Tableau_LP.py` | Simplex von Grund auf | {ref:kap:lp} |
| `Sensitivitaetsanalyse.py` | Schattenpreise | {ref:kap:lp} |
| `Dualitaet_Nachweis.py` | Primal-dual, starke Dualität | {ref:kap:lp} |
| `Toleranzen_und_Entartung.py` | Entartung, Toleranzen, Dualspannen | {ref:kap:lp} |
| `Toleranzen_und_Entartung.py` | Entartung{idx:Entartung}, Toleranzen, Dualspannen | {ref:kap:lp} |
| `Runden_Gegenbeispiel.py` | Warum Runden scheitert | {ref:kap:milp} |
| `Rucksack.py` | Knapsack, LP-Schranke | {ref:kap:milp} |
| `MILP_Portfolio_Fixgebuehren.py` | Fixkosten, Kardinalität | {ref:kap:milp} |
| `Solverstatus_und_Gap.py` | MIP-Gap, Zeitlimit, Statusfälle | {ref:kap:milp} |
| `Solverstatus_und_Gap.py` | MIP-Gap{idx:MIP-Gap}, Zeitlimit, Statusfälle | {ref:kap:milp} |
| `Warmstart_Effekt.py` | LPT-Hinweis für CP-SAT | {ref:kap:milp} |
| `Big_M_Falle.py` | Trickle Flow bei zu großem M | {ref:kap:milp} |
| `Big_M_Falle.py` | Trickle Flow{idx:Trickle Flow} bei zu großem M | {ref:kap:milp} |
| `Propagation_Demo.py` | Propagation messbar | {ref:kap:cpsat} |
| `CP_SAT_Vertretungssystem.py` | Vertretungsplan | {ref:kap:cpsat} |
| `JobShop_Intervalle.py` | Job-Shop-Scheduling | {ref:kap:cpsat} |
| `JobShop_Intervalle.py` | Job-Shop-Scheduling{idx:Job-Shop-Scheduling} | {ref:kap:cpsat} |
| `Parallele_Suche.py` | num_workers: Tempo gegen Reproduzierbarkeit | {ref:kap:cpsat} |
| `CP_SAT_Statusfaelle.py` | Die fünf Solver-Antworten | {ref:kap:cpsat} |
| `Strafgewichte.py` | Gewichte als Wechselkurse | {ref:kap:cpsat} |
| `Min_Cost_Flow.py` | Netzwerkfluss | {ref:kap:graphen} |
| `Zuordnung_Ungarisch.py` | Zuordnung, Unimodularität | {ref:kap:graphen} |
| `VRP_Flotten_Routing.py` | CVRPTW | {ref:kap:graphen} |
| `VRP_Flotten_Routing.py` | CVRPTW{idx:CVRPTW} | {ref:kap:graphen} |
| `VRP_Kapazitaetsfalle.py` | Vergessene Dimension im Routing | {ref:kap:graphen} |
| `Simulated_Annealing.py` | Lokale Suche, Temperatur kalibrieren | {ref:kap:metaheuristiken} |
| `Metaheuristik_vs_Exakt.py` | Der Umschlagpunkt, und die Schranke | {ref:kap:metaheuristiken} |
| `Large_Neighborhood_Search.py` | Zerstören und exakt reparieren | {ref:kap:metaheuristiken} |
| `Spaltengenerierung.py` | Muster statt Stücke, Master und Pricing | {ref:kap:dekomposition} |
| `QP_Grundlagen.py` | Konvexität, DCP-Check | {ref:kap:qp-nlp} |
| `QP_Grundlagen.py` | Konvexität{idx:Konvexität}, DCP-Check | {ref:kap:qp-nlp} |
| `KKT_Nachweis.py` | KKT numerisch prüfen | {ref:kap:qp-nlp} |
| `Entropie_Maximierte_Allokation.py` | NLP mit Entropie | {ref:kap:qp-nlp} |
| `Lokale_Optima_Multistart.py` | Lokale Optima, Multistart | {ref:kap:qp-nlp} |
@ -585,8 +585,8 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Stochastische_Optimierung.py` | Two-Stage mit Recourse | {ref:kap:unsicherheit} |
| `Robuste_Optimierung.py` | Worst-Case-Absicherung | {ref:kap:unsicherheit} |
| `Chance_Constraints.py` | Zusage „mit 95 % Sicherheit“, SOC und Big-M | {ref:kap:unsicherheit} |
| `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion | {ref:kap:dynamische-programmierung} |
| `Mehrziel_Pareto.py` | Pareto-Front, ε-Constraint, Gewichtslücke | {ref:kap:mehrziel} |
| `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion{idx:Rückwärtsinduktion} | {ref:kap:dynamische-programmierung} |
| `Mehrziel_Pareto.py` | Pareto-Front{idx:Pareto-Front}, ε-Constraint, Gewichtslücke | {ref:kap:mehrziel} |
| `Predict_then_Optimize.py` | MSE gegen Entscheidungskosten | {ref:kap:prognose} |
| `Strukturbruecke.py` | Derselbe Code über Werkstatt und Depot | {ref:kap:bruecke} |
| `Kraftwerkseinsatz.py` | Unit Commitment unter Windunsicherheit | {ref:kap:supplychain} |
@ -598,9 +598,9 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Markowitz_CVXPY.py` | GMV, Max Sharpe, Frontier | {ref:kap:markowitz} |
| `Diversifikation_Demo.py` | Korrelation und Portfoliorisiko | {ref:kap:markowitz} |
| `Renditeschaetzung_Falle.py` | Schätzfehler in erwarteten Renditen | {ref:kap:markowitz} |
| `VaR_CVaR_Demo.py` | Fat Tails, Subadditivität | {ref:kap:cvar} |
| `VaR_CVaR_Demo.py` | Fat Tails{idx:Fat Tails}, Subadditivität{idx:Subadditivität} | {ref:kap:cvar} |
| `CVaR_Portfolio.py` | CVaR mit Reibung | {ref:kap:cvar} |
| `QuantitativeTradingEngine.py` | Walk-Forward-Backtest | {ref:kap:handelsmaschine} |
| `QuantitativeTradingEngine.py` | Walk-Forward-Backtest{idx:Walk-Forward-Backtest} | {ref:kap:handelsmaschine} |
| `Backtest_Fallen.py` | Fünf Selbsttäuschungen | {ref:kap:handelsmaschine} |
| `Data_Snooping.py` | Bestes aus N Versuchen auf Rauschen | {ref:kap:handelsmaschine} |
| `Infeasibility_Diagnose.py` | Notfallplan statt Fehler | {ref:kap:praxisfallen} |
@ -613,7 +613,7 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
| `Mutationstest.py` | Findet die Lücken der eigenen Tests | {ref:kap:testing} |
| `Benchmark_Skalierung.py` | Aufbau gegen Lösen, vier Bibliotheken | {ref:kap:testing} |
| `Optimierungsdienst.py` | Das Modell als HTTP-Dienst | {ref:kap:testing} |
| `Konfliktsuche.py` | Deletion Filter: den Widerspruch einkreisen | {ref:anhang:fehlerdiagnose} |
| `Konfliktsuche.py` | Deletion Filter{idx:Deletion Filter}: den Widerspruch einkreisen | {ref:anhang:fehlerdiagnose} |
---
@ -621,7 +621,7 @@ Pfad E oder um gezielt nach einem Thema zu suchen:
Es ist **keine Anleitung zum Bau eigener Solver** — wir nutzen ausgereifte Implementierungen
und investieren die Zeit stattdessen in die Modellierung. Die einzige Ausnahme ist der
Simplex-Algorithmus in {ref:kap:lp}, den wir von Grund auf programmieren, weil man ihn
Simplex-Algorithmus{idx:Simplex-Algorithmus} in {ref:kap:lp}, den wir von Grund auf programmieren, weil man ihn
verstanden haben muss, um Dualität und Schattenpreise zu begreifen.
Es ist **kein Buch über maschinelles Lernen**. Prognosen kommen nur so weit vor, wie sie als

View file

@ -11,21 +11,21 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| Symbol | Sprechweise | Bedeutung | Beispiel aus dem Buch |
| --- | --- | --- | --- |
| $x$ | „iks“ | **Entscheidungsvariable(n)** — das, was der Solver festlegen darf | Anzahl gestarteter Trading-Bots ({ref:kap:einfuehrung}) |
| $x$ | „iks“ | **Entscheidungsvariable{idx:Entscheidungsvariable}(n)** — das, was der Solver festlegen darf | Anzahl gestarteter Trading-Bots ({ref:kap:einfuehrung}) |
| $\mathbf{x}$ | „Vektor iks“ | Alle Entscheidungsvariablen als Spaltenvektor | $\mathbf{x} = (x_1, \dots, x_n)^\top$ |
| $n$ | „en“ | Anzahl der Entscheidungsvariablen | 10 Aktien $\Rightarrow n = 10$ |
| $m$ | „em“ | Anzahl der Nebenbedingungen | 3 Ressourcen $\Rightarrow m = 3$ |
| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der Zielfunktion | Gewinn je Bot-Typ: $(150, 250)^\top$ |
| $\mathbf{A}$ | „Matrix a“ | **Technologiematrix** — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
| $\mathbf{b}$ | „Vektor be“ | **Kapazitätsvektor**, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
| $\mathbf{c}$ | „Vektor ze“ | **Kosten- bzw. Ertragsvektor** der Zielfunktion{idx:Zielfunktion} | Gewinn je Bot-Typ: $(150, 250)^\top$ |
| $\mathbf{A}$ | „Matrix a“ | **Technologiematrix**{idx:Technologiematrix} — wie viel Ressource verbraucht welche Variable | Zeile „RAM“: $(4, 6)$ |
| $\mathbf{b}$ | „Vektor be“ | **Kapazitätsvektor**{idx:Kapazitätsvektor}, „rechte Seite“ der Ungleichungen | $(40, 60)^\top$ vCPU und GB |
| $f(\mathbf{x})$ | „ef von iks“ | **Zielfunktion** — die eine Zahl, die bewertet wird | Tagesgewinn in Euro |
| $g_i(\mathbf{x})$ | „ge i von iks“ | $i$-te **Ungleichungs-Nebenbedingung**, Form $g_i(\mathbf{x}) \le 0$ | „RAM-Verbrauch minus 60 GB“ |
| $h_j(\mathbf{x})$ | „ha j von iks“ | $j$-te **Gleichungs-Nebenbedingung**, Form $h_j(\mathbf{x}) = 0$ | „Summe der Gewichte minus 1“ |
| $s_i$ | „es i“ | **Schlupfvariable** — ungenutzte Reserve der Ressource $i$ | 1 GB RAM übrig $\Rightarrow s_2 = 1$ |
| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / Schattenpreis** der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
| $s_i$ | „es i“ | **Schlupfvariable**{idx:Schlupfvariable} — ungenutzte Reserve der Ressource $i$ | 1 GB RAM übrig $\Rightarrow s_2 = 1$ |
| $y_i,\ \lambda_i$ | „y i“, „lambda i“ | **Dualvariable / Schattenpreis**{idx:Schattenpreis} der Bedingung $i$ | 33,33 € je zusätzlicher Prüfstunde |
| $M$ | „groß em“ | **Big-M** — hinreichend große Konstante zum Ein-/Ausschalten von Bedingungen | $x_j \le M \cdot y_j$ |
| $z_i,\ y_i$ | | **Binärvariable** $\in \{0,1\}$ — Ja/Nein-Schalter | „Position $i$ wird eröffnet“ |
| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge aller erlaubten Lösungen | das Polyeder aus {ref:kap:fundament} |
| $z_i,\ y_i$ | | **Binärvariable**{idx:Binärvariable} $\in \{0,1\}$ — Ja/Nein-Schalter | „Position $i$ wird eröffnet“ |
| $\mathcal{F}$ | „kalligrafisches ef“ | **Zulässiger Bereich** — Menge{idx:Konvexität!Menge} aller erlaubten Lösungen | das Polyeder{idx:Polyeder} aus {ref:kap:fundament} |
| $Z,\ Z^*$ | „zet“, „zet Stern“ | Zielfunktionswert bzw. **optimaler** Zielfunktionswert | $Z^* = 2300$ € |
| $\mathbf{x}^*$ | „iks Stern“ | Die **optimale Lösung**. Der Stern markiert immer „im Optimum“ | $\mathbf{x}^* = (7, 5)^\top$ |
@ -35,7 +35,7 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| --- | --- | --- |
| $\mathbb{R}$ | Reelle Zahlen (beliebig teilbar) | Variable darf 3,7 sein — **LP**, schnell lösbar |
| $\mathbb{R}^n$ | $n$-Tupel reeller Zahlen | Der Raum, in dem $\mathbf{x}$ lebt |
| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **MILP**, NP-schwer |
| $\mathbb{Z}$ | Ganze Zahlen | Variable muss 3 oder 4 sein — **MILP**, NP-schwer{idx:NP-schwer} |
| $\mathbb{N}_0$ | Natürliche Zahlen einschließlich 0 | Stückzahlen, nie negativ |
| $\{0, 1\}$ | Nur zwei Werte | Ja/Nein-Entscheidung |
| $\in$ | „ist Element von“ | $x \in \mathbb{Z}$: „$x$ ist ganzzahlig“ |
@ -49,16 +49,16 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| Symbol | Bedeutung | Lesehilfe |
| --- | --- | --- |
| $\sum_{i=1}^n a_i$ | Summe | „addiere $a_1$ bis $a_n$“ |
| $\mathbf{c}^\top \mathbf{x}$ | Skalarprodukt | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
| $\mathbf{c}^\top \mathbf{x}$ | Skalarprodukt{idx:Skalarprodukt} | „$c_1 x_1 + c_2 x_2 + \dots$“ — eine einzige Zahl |
| $^\top$ | Transponiert | dreht Zeilen und Spalten; macht aus Spalten- einen Zeilenvektor |
| $\mathbf{A}\mathbf{x}$ | Matrix-Vektor-Produkt | „berechne alle Ressourcenverbräuche auf einmal“ |
| $\mathbf{A}\mathbf{x}$ | Matrix-Vektor-Produkt{idx:Matrix-Vektor-Produkt} | „berechne alle Ressourcenverbräuche auf einmal“ |
| $\min_x,\ \max_x$ | Minimum/Maximum über $x$ | „wähle $x$ so, dass … kleinst-/größtmöglich wird“ |
| $\arg\min$ | Argument des Minimums | nicht der *Wert*, sondern die *Stelle* des Minimums |
| $\nabla f$ | „Nabla ef“, Gradient | Vektor aller partiellen Ableitungen — zeigt bergauf |
| $\nabla f$ | „Nabla ef“, Gradient{idx:Gradient} | Vektor aller partiellen Ableitungen — zeigt bergauf |
| $\partial Z / \partial b_i$ | Partielle Ableitung | „wie ändert sich $Z$, wenn nur $b_i$ sich ändert?“ |
| $\Vert \mathbf{v} \Vert_1$ | $L_1$-Norm | Summe der Beträge: $\vert v_1\vert + \vert v_2\vert + \dots$ |
| $\Vert \mathbf{v} \Vert_2$ | $L_2$-Norm (euklidisch) | die gewöhnliche Länge eines Pfeils |
| $\mathbf{P} \succeq 0$ | positiv **semi**definit | alle Eigenwerte $\ge 0$; Funktion ist konvex |
| $\mathbf{P} \succeq 0$ | positiv **semi**definit | alle Eigenwerte $\ge 0$; Funktion{idx:Konvexität!Funktion} ist konvex |
| $\mathbf{P} \succ 0$ | positiv **definit** | alle Eigenwerte $> 0$; Funktion ist *streng* konvex |
| $\mathbb{E}[\cdot]$ | Erwartungswert | gewichteter Mittelwert über alle Szenarien |
| $\mathbb{1}$ oder $\mathbf{1}$ | Einsvektor | $(1,1,\dots,1)^\top$; $\mathbf{1}^\top\mathbf{w}$ ist die Summe aller $w_i$ |
@ -72,15 +72,15 @@ sobald Ihnen ein Symbol oder ein Kürzel begegnet, das Sie nicht sicher zuordnen
| $\mathbf{w}$ | **Portfoliogewichte** — Kapitalanteile je Titel | dimensionslos, Summe $= 1$ |
| $w_i$ | Anteil von Titel $i$ | $0{,}15 \equiv 15\,\%$ |
| $\boldsymbol{\mu}$ | „mü“ — Vektor der **erwarteten Renditen** | z. B. $0{,}11$ = 11 % p. a. |
| $\boldsymbol{\Sigma}$ | „Sigma“ — **Kovarianzmatrix** der Renditen | $N \times N$, Einheit Rendite² |
| $\sigma$ | „sigma“ — **Volatilität** (Standardabweichung) | $0{,}20$ = 20 % p. a. |
| $\boldsymbol{\Sigma}$ | „Sigma“ — **Kovarianzmatrix**{idx:Kovarianzmatrix} der Renditen | $N \times N$, Einheit Rendite² |
| $\sigma$ | „sigma“ — **Volatilität**{idx:Volatilität} (Standardabweichung) | $0{,}20$ = 20 % p. a. |
| $\sigma^2$ | **Varianz** | Quadrat der Volatilität |
| $\mathbf{S}$ | **Stichproben**-Kovarianzmatrix (aus Daten geschätzt) | verrauscht |
| $\mathbf{F}$ | **Shrinkage-Target** — strukturierte Vergleichsmatrix | verzerrt, aber stabil |
| $\delta$ | „delta“ — Shrinkage-Intensität | $\in [0,1]$, z. B. $0{,}18$ |
| $r_f$ | *risk-free rate*, **risikoloser Zins** | z. B. $0{,}03$ = 3 % p. a. |
| $R_{i,t}$ | **Diskrete Rendite** von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
| $r_{i,t}$ | **Logarithmische Rendite** (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
| $R_{i,t}$ | **Diskrete Rendite**{idx:Diskrete Rendite} von Titel $i$ am Tag $t$ | $P_t/P_{t-1} - 1$ |
| $r_{i,t}$ | **Logarithmische Rendite**{idx:Logarithmische Rendite} (stetige Rendite) | $\ln(P_t/P_{t-1})$ |
| $P_{i,t}$ | **Kurs** (bereinigter Schlusskurs) von Titel $i$ | in Währungseinheiten |
| $\lambda$ | Risikoaversion / Gewichtung des Risikoterms | Modellparameter, frei wählbar |
| $\alpha$ | Konfidenzniveau bei VaR/CVaR | $0{,}95$ = „schlechteste 5 % der Tage“ |
@ -100,7 +100,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| --- | --- | --- | --- |
| $\pi$ | „pi“ | **Reihenfolge (Permutation)**: $\pi(k)$ ist der Auftrag, der als $k$-ter läuft | {ref:kap:metaheuristiken} |
| $\Delta$ | „delta“ | **Zugbewertung** — um wie viel ein Nachbarschaftszug die Lösung verschlechtert ($\Delta > 0$) oder verbessert ($\Delta \le 0$) | {ref:kap:metaheuristiken} |
| $T$ | „te“ | **Temperatur** im Simulated Annealing — steuert, wie oft eine Verschlechterung angenommen wird | {ref:kap:metaheuristiken} |
| $T$ | „te“ | **Temperatur** im Simulated Annealing{idx:Simulated Annealing} — steuert, wie oft eine Verschlechterung angenommen wird | {ref:kap:metaheuristiken} |
| $e^{-\Delta/T}$ | | **Annahmewahrscheinlichkeit** (Metropolis-Kriterium): kleine Verschlechterungen fast immer, große fast nie | {ref:kap:metaheuristiken} |
| $\pi_i$ | „pi i“ | **Dualpreis** der Bedingung $i$ im Master-LP — der Preis, mit dem das Pricing rechnet | {ref:kap:dekomposition} |
| $a_i$ | „a i“ | Wie oft Stück $i$ in einem **Schnittmuster** vorkommt (eine Spalte des Masters) | {ref:kap:dekomposition} |
@ -115,7 +115,7 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
> **⚠️ Achtung, Doppelbelegungen.** In der Literatur — und deshalb auch hier — tragen einige
> Buchstaben je nach Kapitel verschiedene Bedeutungen. Die wichtigsten Fälle:
> $\lambda$ ist in {ref:kap:lp} und {ref:kap:qp-nlp} ein **Lagrange-Multiplikator/Schattenpreis**, in {ref:kap:markowitz}
> $\lambda$ ist in {ref:kap:lp} und {ref:kap:qp-nlp} ein **Lagrange-Multiplikator{idx:Lagrange-Multiplikator}/Schattenpreis**, in {ref:kap:markowitz}
> und {ref:kap:cvar} ein **frei gewählter Risikoaversionsparameter**. $\gamma$ ist in {ref:kap:dynamische-programmierung} der
> **Diskontfaktor**, in {ref:kap:cvar} die **VaR-Hilfsvariable**. $S$ bezeichnet als Matrix
> $\mathbf{S}$ die Stichprobenkovarianz, als Skalar $S$ die Szenarienzahl. $\pi$ ist in
@ -140,20 +140,20 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **QP** | Quadratic Program(ming) | Quadratische Optimierung | {ref:kap:qp-nlp} |
| **MIQP** | Mixed-Integer Quadratic Program(ming) | Ganzzahlig-quadratische Optimierung | {ref:kap:markowitz} |
| **NLP** | Nonlinear Program(ming) | Nichtlineare Optimierung | {ref:kap:qp-nlp} |
| **MINLP** | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | {ref:kap:oekosystem} |
| **MINLP**{idx:MINLP} | Mixed-Integer Nonlinear Program(ming) | Ganzzahlig-nichtlineare Optimierung | {ref:kap:oekosystem} |
| **CP** | Constraint Programming | Bedingungsprogrammierung | {ref:kap:cpsat} |
| **SAT** | Boolean **Sat**isfiability Problem | Erfüllbarkeitsproblem der Aussagenlogik | {ref:kap:cpsat} |
| **CP-SAT** | Constraint Programming über SAT-Techniken | Solver-Name von Google OR-Tools | {ref:kap:cpsat} |
| **CDCL** | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | {ref:kap:cpsat} |
| **CP-SAT**{idx:CP-SAT} | Constraint Programming über SAT-Techniken | Solver-Name von Google OR-Tools{idx:OR-Tools} | {ref:kap:cpsat} |
| **CDCL**{idx:CDCL} | Conflict-Driven Clause Learning | konfliktgetriebenes Klausellernen | {ref:kap:cpsat} |
| **DP** | Dynamic Programming | Dynamische Programmierung | {ref:kap:dynamische-programmierung} |
| **B&B** | Branch and Bound | Verzweigen und Beschränken | {ref:kap:milp} |
| **B&C** | Branch and Cut | Verzweigen mit Schnittebenen | {ref:kap:milp} |
| **KKT** | Karush–Kuhn–Tucker (Bedingungen) | Optimalitätsbedingungen bei Restriktionen | {ref:kap:qp-nlp} |
| **SLSQP** | Sequential Least Squares Programming | sequentielle quadratische Optimierung | {ref:kap:qp-nlp} |
| **RMT** | Random Matrix Theory | Zufallsmatrizentheorie | {ref:kap:finanzdaten} |
| **SA** | Simulated Annealing | Simulierte Abkühlung — Metaheuristik, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | {ref:kap:metaheuristiken} |
| **SLSQP**{idx:SLSQP} | Sequential Least Squares Programming | sequentielle quadratische Optimierung | {ref:kap:qp-nlp} |
| **RMT** | Random Matrix Theory | Zufallsmatrizentheorie{idx:Zufallsmatrizentheorie} | {ref:kap:finanzdaten} |
| **SA** | Simulated Annealing | Simulierte Abkühlung — Metaheuristik{idx:Metaheuristik}, die Verschlechterungen mit fallender Wahrscheinlichkeit zulässt | {ref:kap:metaheuristiken} |
| **LNS** | Large Neighborhood Search | Große Nachbarschaftssuche: Teil der Lösung verwerfen, Rest exakt neu bauen | {ref:kap:metaheuristiken} |
| **CG** | Column Generation | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | {ref:kap:dekomposition} |
| **CG** | Column Generation{idx:Column Generation} | Spaltengenerierung — Variablen erst erzeugen, wenn sie sich lohnen | {ref:kap:dekomposition} |
| **IIS** | Irreducible Infeasible Subset | Kleinste widersprüchliche Teilmenge von Bedingungen | {ref:anhang:fehlerdiagnose} |
| **MSE** | Mean Squared Error | Mittlerer quadratischer Fehler — das Standardmaß für Prognosegüte | {ref:kap:prognose} |
@ -165,9 +165,9 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **VRP** | Vehicle Routing Problem | Tourenplanungsproblem | {ref:kap:graphen} |
| **CVRP** | **C**apacitated VRP | VRP mit Fahrzeugkapazitäten | {ref:kap:graphen} |
| **VRPTW** | VRP with **T**ime **W**indows | VRP mit Zeitfenstern | {ref:kap:graphen} |
| **CVRPTW** | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | {ref:kap:graphen} |
| **CVRPTW**{idx:CVRPTW} | Capacitated VRP with Time Windows | mit Kapazitäten *und* Zeitfenstern | {ref:kap:graphen} |
| **MCNFP** | Minimum-Cost Network Flow Problem | Kostenminimales Flussproblem | {ref:kap:graphen} |
| **MTZ** | Miller–Tucker–Zemlin (Formulierung) | Kurzzyklus-Eliminierung | {ref:kap:graphen} |
| **MTZ** | Miller–Tucker–Zemlin (Formulierung) | Kurzzyklus-Eliminierung{idx:Subtour!Eliminierung} | {ref:kap:graphen} |
| **JSSP** | Job-Shop Scheduling Problem | Werkstattfertigungs-Reihenfolgeproblem | {ref:kap:cpsat} |
### 2.3 Finanz- und Risikokennzahlen
@ -177,12 +177,12 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **MPT** | Modern Portfolio Theory | Moderne Portfoliotheorie (Markowitz) | {ref:kap:markowitz} |
| **GMV** | Global Minimum Variance (Portfolio) | Portfolio kleinstmöglicher Varianz | {ref:kap:markowitz} |
| **MVO** | Mean-Variance Optimization | Erwartungswert-Varianz-Optimierung | {ref:kap:markowitz} |
| **SR** | Sharpe Ratio | Überrendite je Einheit Volatilität | {ref:kap:markowitz} |
| **SR** | Sharpe Ratio{idx:Sharpe-Ratio} | Überrendite je Einheit Volatilität | {ref:kap:markowitz} |
| **VaR** | Value at Risk | Verlustschwelle bei gegebener Wahrscheinlichkeit | {ref:kap:cvar} |
| **CVaR** | Conditional Value at Risk | mittlerer Verlust jenseits des VaR | {ref:kap:cvar} |
| **ES** | Expected Shortfall | anderes Wort für CVaR | {ref:kap:cvar} |
| **ES** | Expected Shortfall{idx:Expected Shortfall} | anderes Wort für CVaR | {ref:kap:cvar} |
| **CAGR** | Compound Annual Growth Rate | durchschnittliche jährliche Wachstumsrate | {ref:kap:handelsmaschine} |
| **MDD** | Maximum Drawdown | größter Rückgang vom Höchststand | {ref:kap:handelsmaschine} |
| **MDD** | Maximum Drawdown{idx:Maximum Drawdown} | größter Rückgang vom Höchststand | {ref:kap:handelsmaschine} |
| **LW** | Ledoit–Wolf (Shrinkage) | Schrumpfungsverfahren für Kovarianzmatrizen | {ref:kap:finanzdaten} |
| **p. a.** | *per annum* | pro Jahr | {ref:teil:anwendungen} |
| **bp** | Basispunkt | 0,01 Prozentpunkt; 15 bp = 0,15 % | {ref:kap:cvar}, {ref:kap:handelsmaschine} |
@ -191,19 +191,19 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| Kürzel | Ausgeschrieben | Was es ist | Lizenz |
| --- | --- | --- | --- |
| **HiGHS** | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
| **HiGHS**{idx:HiGHS} | **Hi**gh Performance **G**eneral-purpose **S**olver | LP-, MILP- und QP-Solver in C++ | Open Source (MIT) |
| **OR-Tools** | Google Operations Research Tools | Solver-Sammlung (CP-SAT, Routing, Wrapper) | Open Source (Apache 2.0) |
| **CVXPY** | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
| **CVXPY**{idx:CVXPY} | **C**on**v**e**x** **Py**thon | Modellierungssprache für konvexe Optimierung | Open Source (Apache 2.0) |
| **SciPy** | **Sci**entific **Py**thon | Wissenschaftliches Python-Paket; `scipy.optimize` liefert LP- (über HiGHS), Zuordnungs- (`linear_sum_assignment`) und NLP-Solver (`minimize`) | Open Source (BSD) |
| **GLOP** | **G**oogle **L**inear **Op**timizer | LP-Solver in OR-Tools | Open Source |
| **SCIP** | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
| **SCIP**{idx:SCIP} | Solving Constraint Integer Programs | MILP/MINLP-Solver | akademisch frei |
| **CBC** | **C**oin-or **B**ranch and **C**ut | MILP-Solver des COIN-OR-Projekts | Open Source |
| **GLPK** | GNU Linear Programming Kit | LP/MILP-Solver | Open Source (GPL) |
| **OSQP** | **O**perator **S**plitting **Q**uadratic **P**rogram | QP-Solver | Open Source |
| **ECOS** | Embedded Conic Solver | konischer Solver | Open Source |
| **SCS** | Splitting Conic Solver | konischer Solver | Open Source |
| **IPOPT** | **I**nterior **Po**int **Opt**imizer | NLP-Solver | Open Source (EPL) |
| **Gurobi**, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
| **Gurobi**{idx:Gurobi}, **CPLEX**, **Xpress** | — | kommerzielle Hochleistungs-Solver | kostenpflichtig |
| **API** | Application Programming Interface | Programmierschnittstelle | — |
| **DSL** | Domain-Specific Language | fachspezifische Sprache (hier: Modellierungssprache) | — |
| **CSR** | Compressed Sparse Row | Speicherformat für dünnbesetzte Matrizen | — |
@ -220,12 +220,12 @@ Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinau
| **FEASIBLE** | Zulässige Lösung gefunden, Optimalität nicht bewiesen | Zeitlimit erhöhen oder Gap akzeptieren |
| **INFEASIBLE** | Keine Lösung erfüllt alle Bedingungen | {ref:anhang:fehlerdiagnose}: Diagnose durchführen |
| **UNBOUNDED** | Zielfunktion unbeschränkt verbesserbar | Fehlende Nebenbedingung — Modell prüfen |
| **MIP-Gap** | Relativer Abstand beste Lösung ↔ beste Schranke | 1–2 % sind in der Praxis meist genug |
| **Incumbent** | Beste bisher gefundene zulässige Lösung | Referenz für das Pruning |
| **Relaxation** | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
| **MIP-Gap**{idx:MIP-Gap} | Relativer Abstand beste Lösung ↔ beste Schranke | 1–2 % sind in der Praxis meist genug |
| **Incumbent**{idx:Branch-and-Bound!Incumbent} | Beste bisher gefundene zulässige Lösung | Referenz für das Pruning |
| **Relaxation**{idx:Relaxation} | Modell mit weggelassener Ganzzahligkeit | liefert die Schranke |
| **Warm Start** | Solver startet von bekannter Lösung | beschleunigt wiederholte Läufe |
| **Slack** | Schlupf, ungenutzte Kapazität | Slack = 0 → Engpass |
| **Lookahead-Bias** | Nutzung von Daten aus der Zukunft | Backtest ist wertlos — {ref:kap:praxisfallen} |
| **Lookahead-Bias**{idx:Lookahead-Bias} | Nutzung von Daten aus der Zukunft | Backtest ist wertlos — {ref:kap:praxisfallen} |
---

View file

@ -10,7 +10,7 @@
>
> **Voraussetzungen:** Keine. Dies ist der Einstieg.
>
> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
> **Danach können Sie:** Ein Alltagsproblem in Entscheidungsvariablen, Parameter{idx:Parameter (Modell)}, Zielfunktion{idx:Zielfunktion} und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools{idx:OR-Tools} lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.
>
> **Zeitbedarf:** ca. 3,5 Stunden inklusive Übungen.
>
@ -67,7 +67,7 @@ Block, führen Sie ihn aus, sehen Sie das Ergebnis — die Erklärung kommt dana
geantwortet? Die naheliegende Antwort lautet „möglichst viele Tische“ — der Tisch bringt
mit 240 € schließlich den vierfachen Deckungsbeitrag eines Stuhls. Rechnen wir das durch:
| Strategie | Menge | Deckungsbeitrag |
| Strategie | Menge{idx:Konvexität!Menge} | Deckungsbeitrag |
| --- | --- | ---: |
| Nur Tische (Bauchgefühl) | 40 Tische (das Plattenmaterial ist zuerst leer) | 9 600 € |
| Nur Stühle | 150 Stühle (die Montagezeit ist zuerst leer) | 9 000 € |
@ -200,7 +200,7 @@ Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nic
eine Beschleunigung um den Faktor **eine Million** verschiebt die Grenze von $n=20$ nur auf
etwa $n=24$. Gegen kombinatorisches Wachstum ist Hardware machtlos.
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem
Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem{idx:Zuordnungsproblem (1:1)}
zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und
vergleicht die Laufzeiten.
@ -343,7 +343,7 @@ zusätzlichen Element, die Solver-Zeit wächst kaum merklich.
> | ----------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------ |
> | `itertools.permutations(range(n))` | erzeugt nacheinander alle $n!$ Reihenfolgen | die Definition von „alles durchprobieren“ |
> | `wert = sum(kosten[i][zuordnung[i]] ...)` | bewertet eine einzelne Kombination | das ist die **Zielfunktion**, nur eben von Hand ausgewertet |
> | `modell.NewBoolVar(...)` | legt eine Ja/Nein-**Entscheidungsvariable** an | $n^2$ Variablen statt $n!$ Kombinationen |
> | `modell.NewBoolVar(...)` | legt eine Ja/Nein-**Entscheidungsvariable**{idx:Entscheidungsvariable} an | $n^2$ Variablen statt $n!$ Kombinationen |
> | `modell.AddExactlyOne(...)` | „genau eine davon ist wahr“ | die **Nebenbedingungen** |
> | `modell.Minimize(...)` | die zu minimierende Summe | die **Zielfunktion**, jetzt deklariert statt ausgerechnet |
> | `loeser.Solve(modell)` | der Solver sucht — aber nicht stur | er nutzt Struktur (Schranken, Propagation), statt abzuzählen |
@ -419,7 +419,7 @@ Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen.
> **Die Leitfrage:** *Was ist gegeben und wird nicht entschieden?* Parameter sind der Ort,
> an dem Datenqualität über Modellqualität entscheidet. {ref:kap:finanzdaten} zeigt am Beispiel der
> Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell
> Kovarianzmatrix{idx:Kovarianzmatrix}, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell
> wertlos macht.
### Baustein 3 — Zielfunktion ($f(x)$)
@ -862,8 +862,8 @@ Solver-Ergebnisse wo immer möglich gegen eine unabhängige Rechnung**, gerade a
> | ---------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | Konstanten oben | alle Parameter an einer Stelle | Nie Zahlen im Modellcode verstreuen — sonst findet man sie beim Ändern nicht wieder |
> | `NewIntVar(0, 100, ...)` | ganzzahlige Variable mit weiten Grenzen | Grenzen müssen endlich sein; 100 ist bewusst unkritisch groß |
> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen |
> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten |
> | `modell.Add(x_a <= MAX_ARBITRAGE)` | Marktlimit als **Nebenbedingung** | Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis{idx:Schattenpreis} abfragen |
> | `loeser.StatusName(status)` | echter Status statt fester Text | Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status{idx:Solver-Status} auswerten |
> | `assert ...` | Selbstkontrolle nach dem Lösen | Kostet nichts und fängt Modellierungsfehler, die der Solver nicht bemerken kann |
> | Vergleich mit naiver Strategie | quantifiziert den Nutzen | Die Frage „Was bringt die Optimierung überhaupt?“ kommt in jedem Projekt — beantworten Sie sie ungefragt |
@ -913,7 +913,7 @@ schlägt die Brücke — in beide Richtungen.
> | „Zielzelle“ + Max/Min | `solver.Maximize(...)` | Statt einer Formel in einer Zelle ein Ausdruck im Code. |
> | „Nebenbedingungen“-Liste | `solver.Add(...)` | Eine Zeile je Regel — oder eine Schleife für 10 000 Regeln. |
> | „Ganzzahlig“ / „Binär“ | `NewIntVar(...)` / `NewBoolVar(...)` | Gleiche Bedeutung, gleiche Konsequenz für die Laufzeit. |
> | Simplex-LP / GRG / Evolutionär | GLOP / SLSQP / CP-SAT | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ({ref:kap:oekosystem}). |
> | Simplex-LP / GRG / Evolutionär | GLOP / SLSQP{idx:SLSQP} / CP-SAT{idx:CP-SAT} | In Excel drei Auswahlpunkte, in Python drei Bibliotheken ({ref:kap:oekosystem}). |
> | „Sensitivitätsbericht“ | `bedingung.dual_value()` | Der Schattenpreis — {ref:kap:lp} erklärt, was er bedeutet. |
> | 200 Zeilen sind das Limit | keine praktische Grenze | Der Excel-Solver ist auf 200 veränderbare Zellen begrenzt. |
@ -1103,8 +1103,8 @@ zu und begründen Sie in einem Satz:
(d) „Welche Kunden werden voraussichtlich kündigen?“
(e) „Welchen dieser Kunden sollen wir mit unserem begrenzten Rabattbudget halten?“
**Aufgabe ⭐ — Hart oder weich?**
Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart oder weich
**Aufgabe ⭐ — Hart oder weich{idx:Nebenbedingung!weich}?**
Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart{idx:Nebenbedingung!hart} oder weich
modelliert werden sollte, und begründen Sie:
(a) Gesetzliche Ruhezeit von 11 Stunden zwischen zwei Diensten.
(b) Frau Meier möchte freitags nicht arbeiten.
@ -1269,7 +1269,7 @@ eine Zahl liefern, die betriebswirtschaftlich Unsinn ist. Genau solche Fälle ü
**Ausblick.** {ref:kap:fundament} liefert die Sprache, um Modelle mit hunderten Variablen kompakt
aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich.
Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem
verlässlich lösbar ist: **Konvexität**.
verlässlich lösbar ist: **Konvexität**{idx:Konvexität}.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Das mathematische Fundament — Vektoren, Matrizen, Konvexität {#kap:fundament}
# Kapitel: Das mathematische Fundament — Vektoren, Matrizen, Konvexität{idx:Konvexität} {#kap:fundament}
> **📌 Kapitel auf einen Blick**
>
@ -8,7 +8,7 @@
>
> **Voraussetzungen:** {ref:kap:einfuehrung}. Lineare Algebra wird hier von Grund auf wiederholt.
>
> **Danach können Sie:** Ein Modell in Matrixform aufschreiben, den zulässigen Bereich
> **Danach können Sie:** Ein Modell in Matrixform{idx:Matrixform} aufschreiben, den zulässigen Bereich
> geometrisch deuten, beurteilen ob ein Problem konvex ist — und einschätzen, wie viele
> Stellen Ihres Ergebnisses überhaupt belastbar sind.
>
@ -66,13 +66,13 @@ die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy.
**Das Optimum lautet $(0, 8)$ mit $Z = 40$** — und Sie haben es gefunden, ohne einen
einzigen Solver zu starten. Vier Kandidaten statt unendlich vieler Punkte: Das ist der
**Fundamentalsatz der linearen Optimierung**, und er ist der Grund, warum sich lineare
**Fundamentalsatz der linearen Optimierung**{idx:Fundamentalsatz der linearen Optimierung}, und er ist der Grund, warum sich lineare
Programme überhaupt zuverlässig lösen lassen.
**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes Polyeder** ist — ein
Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion darauf ihren
**Warum funktioniert das?** Weil der zulässige Bereich ein **konvexes Polyeder**{idx:Polyeder} ist — ein
Vielflächner ohne Diagonalen nach innen — und weil eine lineare Zielfunktion{idx:Zielfunktion} darauf ihren
größten Wert immer am Rand annimmt, genauer: in einer Ecke. Beides begründet dieses
Kapitel. Der Simplex-Algorithmus in {ref:kap:lp} tut im Kern nichts anderes als das Programm
Kapitel. Der Simplex-Algorithmus{idx:Simplex-Algorithmus} in {ref:kap:lp} tut im Kern nichts anderes als das Programm
oben, nur klüger: Er probiert nicht alle Ecken durch, sondern läuft gezielt von Ecke zu
Ecke bergauf.
@ -92,12 +92,12 @@ Nach diesem Kapitel können Sie …
2. … erklären, warum der zulässige Bereich eines linearen Programms ein **Polyeder** ist.
3. … den Fundamentalsatz der linearen Optimierung anwenden, um Kandidaten für das Optimum
zu finden, ohne alles durchzuprobieren.
4. … prüfen, ob eine Menge bzw. eine Funktion konvex ist, und begründen, warum das für die
4. … prüfen, ob eine Menge{idx:Konvexität!Menge} bzw. eine Funktion{idx:Konvexität!Funktion} konvex ist, und begründen, warum das für die
Lösbarkeit entscheidend ist.
5. … einen zweidimensionalen Lösungsraum mit `matplotlib` zeichnen und daraus die optimale
Ecke ablesen.
6. … die **Konditionszahl** $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein
schlecht skaliertes Modell mit Ruiz-Equilibrierung wieder rechenbar machen.
6. … die **Konditionszahl**{idx:Konditionszahl} $\kappa(\mathbf{A})$ berechnen, ihren Wert deuten und ein
schlecht skaliertes Modell mit Ruiz-Equilibrierung{idx:Ruiz-Equilibrierung} wieder rechenbar machen.
7. … erklären, warum ein Solver-Ergebnis von `0.99999998` niemals mit `int()` in eine ganze
Zahl verwandelt werden darf.
@ -129,7 +129,7 @@ $$
\mathbf{c} = \begin{pmatrix} c_1 \\ c_2 \\ \vdots \\ c_n \end{pmatrix} \in \mathbb{R}^n
$$
**Zielfunktion als Skalarprodukt:**
**Zielfunktion als Skalarprodukt{idx:Skalarprodukt}:**
$$
f(\mathbf{x}) = \mathbf{c}^\top \mathbf{x} = \sum_{j=1}^n c_j x_j
@ -355,7 +355,7 @@ erzeugt von `bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_fundament_polyeder.s
### Der Fundamentalsatz der linearen Optimierung
> **Satz.** Besitzt ein lineares Optimierungsproblem eine optimale Lösung, dann liegt
> mindestens ein optimaler Punkt auf einer **Ecke (Extrempunkt)** des Polyeders
> mindestens ein optimaler Punkt auf einer **Ecke (Extrempunkt)**{idx:Ecke (Extrempunkt)} des Polyeders
> $\mathcal{F}$.
**Warum das gilt — die Anschauung:** Die Zielfunktion $\mathbf{c}^\top\mathbf{x}$ hat
@ -475,8 +475,8 @@ etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.
| --- | --- | --- |
| Lineares Programm (LP) | **ja** | Globales Optimum, Sekunden bis Minuten selbst bei $10^6$ Variablen |
| Quadratisches Programm mit $\mathbf{P} \succeq 0$ | **ja** | Globales Optimum, effizient ({ref:kap:qp-nlp}, {ref:kap:markowitz}) |
| Ganzzahlige Variablen (MILP) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — NP-schwer ({ref:kap:milp}) |
| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige Kovarianzmatrix ist ein typischer Auslöser (siehe {ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}) |
| Ganzzahlige Variablen (MILP) | **nein** | Der zulässige Bereich ist ein Punktgitter, keine zusammenhängende Menge — NP-schwer{idx:NP-schwer} ({ref:kap:milp}) |
| Quadratisches Programm mit indefinitem $\mathbf{P}$ | **nein** | Nur lokale Optima; eine ungültige Kovarianzmatrix{idx:Kovarianzmatrix} ist ein typischer Auslöser (siehe {ref:sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}) |
| Allgemeines NLP mit beliebigen Funktionen | meist **nein** | `scipy.optimize` liefert nur ein lokales Optimum, abhängig vom Startpunkt |
### Konvexität sichtbar machen
@ -893,7 +893,7 @@ Einheit messen“. Die Lösung rechnet man mit $\mathbf{x} = \mathbf{D}_c\,\tild
zurück.
> **🎯 Merksatz**
> Jeder ernsthafte Solver skaliert intern selbst — HiGHS, Gurobi, CP-SAT alle. Verlassen Sie
> Jeder ernsthafte Solver skaliert intern selbst — HiGHS{idx:HiGHS}, Gurobi{idx:Gurobi}, CP-SAT{idx:CP-SAT} alle. Verlassen Sie
> sich trotzdem nicht darauf: Die interne Skalierung repariert die Rechnung, nicht die
> Modellierung. Wenn Ihre Koeffizienten zwölf Größenordnungen überspannen, sagt Ihnen das
> etwas über Ihr Modell, nicht über den Solver.
@ -902,7 +902,7 @@ zurück.
Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und
schließt mit der praktischen Konsequenz, die Sie in {ref:kap:milp} wieder brauchen werden:
warum eine Binärvariable mit dem Wert `0.99999998` niemals mit `int()` gerundet werden darf.
warum eine Binärvariable{idx:Binärvariable} mit dem Wert `0.99999998` niemals mit `int()` gerundet werden darf.
```python
#!/usr/bin/env python3

View file

@ -57,8 +57,8 @@
> Weizen 0.750 kg, Soja 0.250 kg -> 0.5350 EUR/kg
> ```
**Sie haben soeben HiGHS benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter
CVXPY und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die
**Sie haben soeben HiGHS{idx:HiGHS} benutzt** — denselben C++-Solver, der auch hinter `highspy`, hinter
CVXPY{idx:CVXPY} und in vielen kommerziellen Systemen steckt. `scipy.optimize.linprog` ist nur die
dünnste denkbare Hülle darum.
Das ist die zentrale Botschaft dieses Kapitels: **Modellierungsschicht und Solver sind zwei
@ -68,13 +68,13 @@ lassen sich unabhängig voneinander tauschen — und genau davon handelt der Res
> **⚠️ Zwei Stolpersteine stecken schon in diesen sechs Zeilen**
>
> * `linprog` **minimiert immer**. Wer maximieren will, negiert die Zielfunktion — und darf
> * `linprog` **minimiert immer**. Wer maximieren will, negiert die Zielfunktion{idx:Zielfunktion} — und darf
> nicht vergessen, das Ergebnis zurückzudrehen.
> * `linprog` kennt **nur `<=`**. Ein Mindestgehalt „$\ge 20$“ wird zu „$-12x_1 - 44x_2 \le
> -20$“. Ein Vorzeichenfehler an dieser Stelle liefert eine perfekt aussehende Lösung, die
> das Gegenteil des Gewollten erfüllt.
>
> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo
> Beide Fallen verschwinden, sobald man eine Modellierungsschicht wie CVXPY oder Pyomo{idx:Pyomo}
> benutzt, in der `>=` einfach `>=` heißt. Das ist ein Hauptgrund, warum es sie gibt.
---
@ -89,8 +89,8 @@ Nach diesem Kapitel können Sie …
formulieren.
4. … Ergebnisse verschiedener Solver gegeneinander validieren (*Cross-Check*).
5. … einschätzen, wann sich der Aufwand einer Low-Level-Schnittstelle lohnt und wann nicht.
6. … Pyomo und Linopy einordnen und begründen, wann sich der Umstieg auf sie lohnt.
7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars
6. … Pyomo und Linopy{idx:Linopy} einordnen und begründen, wann sich der Umstieg auf sie lohnt.
7. … Aufbau- und Lösezeit getrennt messen und einen Modellaufbau mit NumPy oder Polars{idx:Polars}
vektorisieren, statt ihn in Python-Schleifen zu erzeugen.
---
@ -115,7 +115,7 @@ nutzt eine **zweischichtige Architektur**{idx:Zweischichtige Architektur}:
> mit fünf verschiedenen Solvern lösen.
**Warum diese Trennung nützlich ist:** Ein CVXPY{idx:CVXPY}-Modell, das heute mit dem freien Solver
Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi — man tauscht
Clarabel läuft, läuft morgen ohne Codeänderung mit dem kommerziellen Gurobi{idx:Gurobi} — man tauscht
ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein anzufangen.
---
@ -125,10 +125,10 @@ ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein a
| Bibliothek | Stärken & Problemklassen | Wann einsetzen? | Typische Backends |
| --- | --- | --- | --- |
| `scipy.optimize` | Standard-LPs, kontinuierliche nichtlineare Optimierung, Wurzelsuche | Für elementare LPs und schnelle Prototypen ohne zusätzliche schwere Abhängigkeiten | HiGHS (`highs-ds`, `highs-ipm`) |
| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing (VRP/TSP), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | CP-SAT, GLOP, Routing Engine |
| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, CVaR, $L_1$-Transaktionskosten | Clarabel, OSQP, ECOS, SCS, HiGHS |
| `ortools` (Google) | Diskrete Planung, Schichten, Vertretungspläne, Routing (VRP/TSP), kombinatorische Logik | Wenn diskrete Ja/Nein-Entscheidungen, Zeitfenster und Wenn-Dann-Regeln dominieren | CP-SAT{idx:CP-SAT}, GLOP, Routing Engine |
| `cvxpy` | Konvexe Optimierung, quadratische Programme, Risikomodelle, Portfolios | **Erste Wahl für Finanzoptimierung**, Markowitz, CVaR, $L_1$-Transaktionskosten{idx:Transaktionskosten} | Clarabel, OSQP, ECOS, SCS, HiGHS |
| `highspy` | Direkte Solver-Steuerung für LP/MILP, maximale Kontrolle | Wenn Sie Modelle wiederholt und schnell aufbauen oder Solver-Details steuern müssen | HiGHS |
| `Pyomo` | Universelle Modellierungssprache (LP, MILP, MINLP) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, SCIP, IPOPT |
| `Pyomo` | Universelle Modellierungssprache (LP, MILP, MINLP{idx:MINLP}) | Industrielle Großmodelle mit strikter Trennung von Daten und Modell | HiGHS, Gurobi, CPLEX, SCIP{idx:SCIP}, IPOPT |
| `Linopy` | LP/MILP über beschriftete Arrays (`xarray`) | Modelle mit zehntausenden gleichartigen Nebenbedingungen (Energie, Netze, Zeitreihen) | HiGHS, GLPK, CBC, Gurobi |
### Die Entscheidung in drei Fragen
@ -136,7 +136,7 @@ ein Argument. Das schützt vor Herstellerbindung und erlaubt, im Projekt klein a
Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:
> **Frage 1: Gibt es Ja/Nein-Entscheidungen oder Reihenfolgen?**
> → **Ja:** OR-Tools (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei
> → **Ja:** OR-Tools{idx:OR-Tools} (CP-SAT) bei Zuweisung/Scheduling, oder MILP über HiGHS bei
> ökonomischen Fixkostenmodellen.
> → **Nein:** weiter zu Frage 2.
>
@ -315,7 +315,7 @@ Zeichenkette an `python -c` übergeben — funktioniert und ist trotzdem die sch
Der Schnipsel ist für Editor, Linter und Testwerkzeug unsichtbar, ein Tippfehler darin
fällt erst zur Laufzeit auf, und übergeben lassen sich nur Zeichenketten.
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion mit lokalem Import**,
Tragfähig ist stattdessen: **jeder Solver eine gewöhnliche Funktion{idx:Konvexität!Funktion} mit lokalem Import**,
ausgeführt von einem `ProcessPoolExecutor` mit zwei Einstellungen, die zusammen die
Garantie ergeben:
@ -517,8 +517,8 @@ Alle Wege fuehren zum selben, von Hand bestaetigten Optimum.
>
> | Ansatz | Zeilen für das Modell | Charakter |
> | --- | --- | --- |
> | `linprog` | 3 | Matrizen direkt übergeben. Kürzeste Variante, aber man muss selbst negieren und die Matrixform von Hand herstellen. |
> | `highspy` | ~15 | Alles explizit, inklusive CSR-Format der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
> | `linprog` | 3 | Matrizen direkt übergeben. Kürzeste Variante, aber man muss selbst negieren und die Matrixform{idx:Matrixform} von Hand herstellen. |
> | `highspy` | ~15 | Alles explizit, inklusive CSR-Format{idx:CSR-Format} der dünnbesetzten Matrix. Aufwendig — dafür volle Kontrolle und kein Overhead beim wiederholten Lösen. |
> | `cvxpy` | 4 | Liest sich wie die mathematische Formulierung. Prüft zusätzlich automatisch, ob das Problem konvex ist. Höchster Startaufwand pro Lauf (Kompilierung des Ausdrucksbaums). |
> | `ortools`/GLOP | ~6 | Bedingungen einzeln mit `Add()` — gut lesbar bei wenigen, mühsam bei vielen Restriktionen. |
>
@ -569,7 +569,7 @@ große LPs überhaupt lösbar sind.
| --- | --- | --- |
| Einmaliges kleines LP, Prototyp | `scipy.optimize.linprog` | Keine zusätzliche Abhängigkeit, 3 Zeilen |
| Portfolio, Risiko, alles Konvexe | **CVXPY** | Lesbarkeit + automatische Konvexitätsprüfung |
| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | Globale Constraints, Konfliktlernen |
| Dienstpläne, Zuordnung, Reihenfolge | **CP-SAT** | Globale Constraints{idx:Globale Constraints}, Konfliktlernen |
| Fahrzeugtouren | **OR-Tools Routing** | Fertige Metaheuristiken, jahrzehntelang optimiert |
| MILP mit Fixkosten, Kardinalität | `highspy` oder CP-SAT | Branch-and-Cut auf ökonomischer Struktur |
| 50 000+ Variablen, wiederholte Läufe | `highspy` oder Pyomo | Modellaufbau wird sonst zum Engpass |
@ -601,7 +601,7 @@ modell.kapazitaet = pyo.Constraint(modell.R, rule=kapazitaet)
```
Das ist das $\forall i \in I$ aus {ref:kap:einfuehrung}, unmittelbar in Code übersetzt: eine
Regel, angewandt auf jedes Element einer Menge. Pyomo kann außerdem, was CVXPY und OR-Tools
Regel, angewandt auf jedes Element einer Menge{idx:Konvexität!Menge}. Pyomo kann außerdem, was CVXPY und OR-Tools
nicht können — **nichtlineare und gemischt-ganzzahlig-nichtlineare** Modelle (MINLP) an
Solver wie Ipopt oder BONMIN übergeben (siehe {ref:kap:qp-nlp}).
@ -1225,7 +1225,7 @@ passende Empfehlungen.
> **🐛 Finde den Denkfehler: Der Solver, der angeblich dreimal schneller ist**
>
> Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem und schreibt ins
> Ein Team vergleicht zwei Bibliotheken für sein Zuordnungsproblem{idx:Zuordnungsproblem (1:1)} und schreibt ins
> Protokoll: *„CVXPY braucht 1,9 s, SciPy nur 0,6 s — wir setzen auf SciPy.“* Gemessen
> wurde so:
>
@ -1267,7 +1267,7 @@ passende Empfehlungen.
>
> **1. Sie sollen 300 Prüfungen auf 40 Räume und 12 Zeitfenster verteilen, mit Regeln wie
> „diese beiden Klausuren nicht gleichzeitig“. Welches Werkzeug passt?**
> (a) CVXPY — es prüft die Konvexität automatisch.
> (a) CVXPY — es prüft die Konvexität{idx:Konvexität} automatisch.
> (b) OR-Tools CP-SAT — diskrete Zuweisung mit logischen Regeln ist genau sein Gebiet.
> (c) `scipy.optimize.minimize` — es kann beliebige Zielfunktionen.
>
@ -1280,7 +1280,7 @@ passende Empfehlungen.
>
> **3. Was haben `scipy.optimize.linprog`, `highspy`, CVXPY (mit Standardeinstellung) und
> Pyomo (mit `appsi_highs`) gemeinsam?**
> (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus in Python.
> (a) Sie implementieren jeweils einen eigenen Simplex-Algorithmus{idx:Simplex-Algorithmus} in Python.
> (b) Sie sind Modellierungsschichten über demselben C++-Solver HiGHS — die Rechenleistung
> ist dieselbe, nur die Schreibweise unterscheidet sich.
> (c) Sie können alle sowohl konvexe als auch nicht-konvexe Probleme global lösen.

View file

@ -80,7 +80,7 @@ Monatsbericht steht.
> Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die
> Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.
**Warum funktioniert das?** Weil eine Zielfunktion eine **vollständige** Aussage darüber ist,
**Warum funktioniert das?** Weil eine Zielfunktion{idx:Zielfunktion} eine **vollständige** Aussage darüber ist,
was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger
wichtig", sondern **wertlos**. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit;
er sieht ihn schlicht nicht.
@ -96,7 +96,7 @@ Nach diesem Kapitel können Sie …
2. … die vier Fragen stellen, die aus einer Anforderung ein Modell machen.
3. … erkennen, dass ein Satz wie „Stammkunden dürfen wir nicht verlieren" mehrere Lesarten
hat, und den Unterschied in Euro beziffern.
4. … begründen, wann eine Regel hart und wann sie weich sein muss — und was eine harte Regel
4. … begründen, wann eine Regel hart{idx:Nebenbedingung!hart} und wann sie weich{idx:Nebenbedingung!weich} sein muss — und was eine harte Regel
im Betrieb anrichtet, wenn sie einmal nicht erfüllbar ist.
5. … einen Kennzahlen-Fehlanreiz benennen, bevor er in die Zielfunktion gerät.
6. … einen Satz aus einer Besprechung in den passenden Modellierungsbaustein
@ -112,7 +112,7 @@ Was aus einer Besprechung mitkommt, klingt meist so:
> *„Wir müssen die Maschinen besser auslasten. Und die Stammkunden dürfen wir nicht
> verlieren."*
Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable,
Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable{idx:Entscheidungsvariable},
keine messbare Zielgröße, keine Nebenbedingung. Sie enthalten nicht einmal genug, um zu
erkennen, *was* entschieden werden soll.
@ -142,7 +142,7 @@ sie übersprungen.
Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14
Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300.
Es ist bewusst das **einfachste Modell des ganzen Buchs** — eine Binärvariable je Auftrag,
Es ist bewusst das **einfachste Modell des ganzen Buchs** — eine Binärvariable{idx:Binärvariable} je Auftrag,
eine Nebenbedingung. Alles, was sich zwischen den fünf Varianten ändert, ist die Frage.
```python
@ -547,7 +547,7 @@ Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.
### Teil 3: Hart oder weich
Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:
Dieselbe Regel als Strafkosten{idx:Strafkosten} je abgelehntem Stammkundenauftrag:
| Strafe je Auftrag | Deckungsbeitrag | abgelehnte Stammaufträge |
| ---: | ---: | ---: |
@ -639,26 +639,26 @@ es **bedeutet**, und welcher **Baustein** daraus wird. Die B-Nummern verweisen a
| Was gesagt wird | Was gemeint ist | Baustein |
| --- | --- | --- |
| „Wir haben nur 400 Stunden." | eine Obergrenze, die nicht überschritten werden darf | harte Kapazitätsgrenze $\le b$ ({ref:kap:lp}) |
| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | Budgetlimit (B27) |
| „Mehr als zwei Millionen gibt es nicht." | dasselbe, auf Geld | Budgetlimit{idx:Budgetlimit} (B27) |
| „Möglichst nicht über 40 Stunden." | eine Grenze, die im Notfall gebrochen werden darf | weiche Grenze mit Strafkosten (B8) |
| „Wir können nur ganze Kisten liefern." | die Menge ist nicht teilbar | Ganzzahligkeitsbedingung ({ref:kap:milp}) |
| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | Aktivierungsschalter (B1) |
| „Wir können nur ganze Kisten liefern." | die Menge{idx:Konvexität!Menge} ist nicht teilbar | Ganzzahligkeitsbedingung ({ref:kap:milp}) |
| „Wenn wir die Anlage nutzen, fallen Fixkosten an." | Kosten, die erst ab der ersten Einheit anfallen | Aktivierungsschalter{idx:Aktivierungsschalter (Modellierungsmuster)} (B1) |
| „Entweder gar nicht oder mindestens 50." | kein Betrieb im Kleinen | semikontinuierliche Variable (B2) |
| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | Implikation (B3) |
| „Wenn wir A bauen, brauchen wir auch B." | eine Folgeverpflichtung | Implikation{idx:Implikation (Modellierungsmuster)} (B3) |
| „Höchstens drei Positionen im Depot." | eine Obergrenze auf die **Anzahl**, nicht die Menge | Kardinalität (B6) |
| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | Verhältnis-Bedingung (B12) |
| „Der Anteil muss mindestens 30 % betragen." | eine Bedingung auf ein **Verhältnis** | Verhältnis-Bedingung{idx:Verhältnis-Bedingung} (B12) |
| „Jeder Auftrag genau einem Mitarbeiter." | eine vollständige, eindeutige Zuordnung | Zuordnung 1:1 (B23) |
| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | Nichtüberlappung (B14) |
| „B kann erst nach A." | eine Reihenfolge | Vorrangbeziehung (B13) |
| „Eine Maschine kann nur eines gleichzeitig." | zeitliche Ausschließlichkeit | Nichtüberlappung{idx:Nichtüberlappung} (B14) |
| „B kann erst nach A." | eine Reihenfolge | Vorrangbeziehung{idx:Vorrangbeziehung} (B13) |
| „Höchstens fünf Tage am Stück." | eine Bedingung über ein **gleitendes Fenster** | gleitendes Fenster (B16) |
| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | Umrüstkosten (B17), Rüstzeit als Kapazität (B26) |
| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | Flusserhaltung (B22) |
| „Umstellen kostet uns eine halbe Stunde." | reihenfolgeabhängige Kosten | Umrüstkosten{idx:Umrüstkosten (Modellierungsmuster)} (B17), Rüstzeit{idx:Rüstzeit} als Kapazität (B26) |
| „Was reinkommt, muss auch wieder raus." | Erhaltung an jedem Knoten | Flusserhaltung{idx:Flusserhaltung} (B22) |
| „Notfalls brechen wir die Regel." | die Bedingung ist weich, aber teuer | Schlupfvariablen (B18) |
| „Erst Termintreue, dann Kosten." | eine Rangfolge zwischen Zielen | hierarchische Ziele (B19) |
| „Es soll gerecht zugehen." | den Schlechtestgestellten verbessern | Min/Max in der Zielfunktion (B11) |
| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | Schattenpreis ({ref:kap:lp}, {ref:sec:praxisfallen-attribution}) |
| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | Chance Constraint ({ref:sec:unsicherheit-chance-constraints}) |
| „Wir sichern uns gegen Schätzfehler ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug (B21) |
| „Was brächte uns eine zusätzliche Stunde?" | die Frage nach dem Grenzwert einer Ressource | Schattenpreis{idx:Schattenpreis} ({ref:kap:lp}, {ref:sec:praxisfallen-attribution}) |
| „Mit 95 % Sicherheit muss es halten." | eine Zusage auf eine **Quote**, nicht auf einen Mittelwert | Chance Constraint{idx:Chance Constraint} ({ref:sec:unsicherheit-chance-constraints}) |
| „Wir sichern uns gegen Schätzfehler{idx:Schätzfehler} ab." | gegen die ungünstigste Parameterlage planen | Worst-Case-Abzug{idx:Robuste Optimierung!Worst-Case-Abzug} (B21) |
### Fünf Wendungen, bei denen die Übersetzung eine Entscheidung erzwingt
@ -690,7 +690,7 @@ jemandem erklärt werden muss, der das Modell nicht kennt:
| Optimalitätslücke von 2 % | „Besser als das hier geht es höchstens um 2 % — und die liegen unter unserer Datenungenauigkeit." |
| CVaR | „Wenn es schiefgeht, verlieren wir im Mittel so viel." (Der VaR sagt nur, **ob** es schiefgeht.) |
| EVPI | „Selbst eine perfekte Prognose wäre uns höchstens so viel wert." |
| Relaxation | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
| Relaxation{idx:Relaxation} | „Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet." |
| Chance Constraint | „Der Plan hält in 95 von 100 Fällen — und der nächste Prozentpunkt kostet …" |
---

View file

@ -8,9 +8,9 @@ Programmieren zu tun hat.
| Frage | Kapitel | Was Sie danach können |
| --- | --- | --- |
| Was ist überhaupt ein Optimierungsmodell? | {ref:kap:einfuehrung} | Die vier Bausteine benennen — Variablen, Zielfunktion, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | {ref:kap:fundament} | Konvexität beurteilen, den zulässigen Bereich als Polyeder sehen, schlechte Konditionierung erkennen und beheben |
| Welche Bibliothek, und warum vertragen sie sich nicht? | {ref:kap:oekosystem} | Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
| Was ist überhaupt ein Optimierungsmodell? | {ref:kap:einfuehrung} | Die vier Bausteine benennen — Variablen, Zielfunktion{idx:Zielfunktion}, Nebenbedingungen, Daten — und sie in einem fremden Problem wiedererkennen |
| Warum rechnet der Solver falsch, obwohl das Modell stimmt? | {ref:kap:fundament} | Konvexität{idx:Konvexität} beurteilen, den zulässigen Bereich als Polyeder{idx:Polyeder} sehen, schlechte Konditionierung erkennen und beheben |
| Welche Bibliothek, und warum vertragen sie sich nicht? | {ref:kap:oekosystem} | Solver, Bindings und Modellierungsschichten unterscheiden — und den HiGHS{idx:HiGHS}-Symbolkonflikt zwischen `ortools` und `highspy` umgehen |
| Wie wird aus einem Satz ein Modell? | {ref:kap:modellierung} | Die vier Fragen stellen, harte von weichen Regeln trennen, einen Besprechungssatz übersetzen ({ref:sec:modellierung-glossar}) |
## Die Reihenfolge ist die Aussage

View file

@ -11,12 +11,12 @@ Fragen:
> **🎯 Merksatz zum Diagramm**
> Die erste Frage ist die wichtigste — und sie lautet **nicht** „welcher Solver ist der
> schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein Zuordnungsproblem
> schnellste?“, sondern „welche **Struktur** hat mein Problem?“. Ein Zuordnungsproblem{idx:Zuordnungsproblem (1:1)}
> braucht keinen MILP-Solver, sondern eine Zeile `linear_sum_assignment`. Ein Dienstplan
> gehört nicht in ein LP, sondern zu CP-SAT. Wer die Struktur erkennt, hat die halbe
> gehört nicht in ein LP, sondern zu CP-SAT{idx:CP-SAT}. Wer die Struktur erkennt, hat die halbe
> Laufzeit gespart, bevor die erste Zeile Code geschrieben ist.
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion, unsichere Daten,
Fällt Ihr Problem durch das Diagramm hindurch — nichtlineare Zielfunktion{idx:Zielfunktion}, unsichere Daten,
Entscheidungen über mehrere Zeitstufen —, dann ist {ref:teil:nichtlinear} der richtige
Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
@ -27,14 +27,14 @@ Ort. Dort steht ein zweites Diagramm für genau diese Fälle.
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Um das Kernverfahren des Operations Research. Wir bauen den
> Simplex-Algorithmus selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher
> Simplex-Algorithmus{idx:Simplex-Algorithmus} selbst — nicht um ihn zu verwenden, sondern um zu verstehen, woher
> **Schattenpreise** kommen. Sie sind das wertvollste Nebenprodukt jeder Optimierung.
>
> **Voraussetzungen:** {ref:kap:fundament} (Matrixform, Polyeder, Ecken).
> **Voraussetzungen:** {ref:kap:fundament} (Matrixform{idx:Matrixform}, Polyeder, Ecken).
>
> **Danach können Sie:** Ein LP in Standardform bringen, den Simplex von Hand rechnen,
> Dualwerte interpretieren, daraus wirtschaftliche Handlungsempfehlungen ableiten — und
> erkennen, wann ein Schattenpreis eine Entscheidung **nicht** tragen darf.
> erkennen, wann ein Schattenpreis{idx:Schattenpreis} eine Entscheidung **nicht** tragen darf.
>
> **Zeitbedarf:** ca. 7 Stunden — das umfangreichste Kapitel von {ref:teil:kernverfahren}.
>
@ -124,13 +124,13 @@ Nach diesem Kapitel können Sie …
1. … Schlupfvariablen einführen und ein Ungleichungssystem in ein Gleichungssystem
überführen.
2. … eine Simplex-Iteration von Hand rechnen: Pivotspalte, Pivotzeile, Basistausch.
2. … eine Simplex-Iteration von Hand rechnen: Pivotspalte{idx:Simplex-Algorithmus!Pivotspalte}, Pivotzeile, Basistausch.
3. … erklären, warum das Verfahren terminiert und warum es das Optimum findet.
4. … das duale Problem zu einem gegebenen primalen aufstellen.
5. … **Schattenpreise**{idx:Schattenpreis} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle,
die einer der häufigsten Fehler bei der Sensitivitätsanalyse ist.
5. … **Schattenpreise**{idx:Schattenpreis} korrekt auslesen und interpretieren — inklusive der Vorzeichenfalle{idx:Schattenpreis!Vorzeichenfalle},
die einer der häufigsten Fehler bei der Sensitivitätsanalyse{idx:Sensitivitätsanalyse} ist.
6. … aus einer Sensitivitätsanalyse eine begründete Kaufentscheidung ableiten.
7. … ein Modell auf **Entartung** prüfen und statt eines nicht eindeutigen Schattenpreises
7. … ein Modell auf **Entartung**{idx:Entartung} prüfen und statt eines nicht eindeutigen Schattenpreises
dessen belastbare Spanne berechnen.
8. … erklären, warum Zulässigkeit immer gegen eine Toleranz und nie mit `== 0` geprüft wird.
@ -167,7 +167,7 @@ $$
| $s_i > 0$ | Es sind $s_i$ Einheiten **übrig** | nicht-bindende Nebenbedingung, Reserve |
> **🎯 Merksatz**
> Die Schlupfvariable ist kein Rechentrick, sondern eine Messgröße: Sie sagt Ihnen, welche
> Die Schlupfvariable{idx:Schlupfvariable} ist kein Rechentrick, sondern eine Messgröße: Sie sagt Ihnen, welche
> Ressource Ihr Geschäft begrenzt. Und nur wo sie null ist, kann es sich lohnen, Kapazität
> nachzukaufen.
@ -182,12 +182,12 @@ Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
![Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über
(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der
ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der
Zielfunktion, der Pfeil ihr Gradient $\mathbf{c}$. Erzeugt von
Zielfunktion, der Pfeil ihr Gradient{idx:Gradient} $\mathbf{c}$. Erzeugt von
`bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
**Was Sie in der Abbildung sehen.** Durch jede besuchte Ecke läuft genau eine Niveaulinie —
die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken.
Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es
Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder{idx:Polyeder} es
zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des
Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
@ -506,7 +506,7 @@ und 31,25 €.
> theoretisch endlos kreisen. In der Praxis extrem selten; Bland's Regel verhindert es
> beweisbar.
> * **Den Simplex selbst produktiv einsetzen.** Diese Implementierung ist ein Lehrmodell.
> HiGHS ist um Größenordnungen schneller und numerisch stabiler.
> HiGHS{idx:HiGHS} ist um Größenordnungen schneller und numerisch stabiler.
---
@ -938,7 +938,7 @@ Deckungsbeitrag, also 15,33 € Reingewinn.
> 2. **Numerische Gegenprobe:** Das Modell wird mit $b_3 + 1$ neu gelöst; der gemessene
> Zuwachs muss dem Schattenpreis entsprechen. Diese Prüfung ist unabhängig von jeder
> Vorzeichenkonvention und deshalb der zuverlässigste Test überhaupt.
> 3. **Komplementärer Schlupf:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
> 3. **Komplementärer Schlupf{idx:Komplementärer Schlupf}:** $s_i \cdot y_i = 0$ muss für alle $i$ gelten.
>
> **Übernehmen Sie dieses Muster in eigene Projekte.** Der Aufwand ist eine Handvoll Zeilen,
> der Nutzen ist die Gewissheit, dass Ihre Handlungsempfehlung nicht das Gegenteil des
@ -992,7 +992,7 @@ Das ist keine theoretische Sorge. Dasselbe Modell, zwei Verfahren desselben Solv
| Verfahren | Schattenpreise | Was das Management daraus liest |
| --- | --- | --- |
| Dual Simplex | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
| Dual Simplex{idx:Dual Simplex} | $(0{,}33;\ 0{,}33;\ 0)$ | „Fräse und Schleiferei sind die Engpässe, die Prüfung ist wertlos.“ |
| Innere-Punkte-Verfahren | $(0;\ 0;\ 1{,}00)$ | „Die Prüfung ist der Engpass, Fräse und Schleiferei sind wertlos.“ |
**Beide Zeilen sind mathematisch korrekt.** Sie widersprechen sich trotzdem vollständig.
@ -1021,7 +1021,7 @@ nicht `== 0.0`. Wer exakt vergleicht, übersieht ausgerechnet die Engpässe, die
### Die ehrliche Auskunft: Schattenpreis-Spannen
Was tut man also, wenn das Modell entartet ist? Man meldet keinen Einzelwert, sondern die
**Spanne**. Die Menge aller optimalen Dualvektoren ist selbst ein Polyeder:
**Spanne**. Die Menge{idx:Konvexität!Menge} aller optimalen Dualvektoren ist selbst ein Polyeder:
$$
\mathbf{A}^\top\mathbf{y} \ge \mathbf{c},\qquad \mathbf{y} \ge \mathbf{0},
@ -1318,7 +1318,7 @@ $$\max\ 5x_1 + 4x_2 \quad \text{u. d. N.}\quad 6x_1+4x_2 \le 24,\quad x_1+2x_2 \
Geben Sie jedes Zwischentableau an und lesen Sie am Ende Lösung **und** Schattenpreise ab.
Prüfen Sie mit `scipy.optimize.linprog`.
**Aufgabe ⭐⭐ — Duales Problem aufstellen.**
**Aufgabe ⭐⭐ — Duales Problem{idx:Duales Problem} aufstellen.**
Stellen Sie zum Modell aus der Aufgabe *Simplex von Hand* das duale Problem auf, lösen Sie es mit `linprog` und
weisen Sie den starken Dualitätssatz numerisch nach.
@ -1367,7 +1367,7 @@ $$\max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,
> (a) Der Schattenpreis ist $y_i = \partial Z^* / \partial b_i$. Welche Eigenschaft einer
> Ableitung übersieht die Hochrechnung auf 2 000 Stunden?
>
> (b) Skizzieren Sie, wie der zusätzliche Deckungsbeitrag als Funktion der zusätzlichen
> (b) Skizzieren Sie, wie der zusätzliche Deckungsbeitrag als Funktion{idx:Konvexität!Funktion} der zusätzlichen
> Ofenstunden tatsächlich verläuft. Warum ist er nicht linear — und in welche Richtung
> liegt der Fehler?
>
@ -1444,7 +1444,7 @@ $$\max\ 3x_1+2x_2 \quad\text{u. d. N.}\quad x_1+x_2 \ge 4,\quad x_1+3x_2 \le 12,
**Ausblick.** {ref:kap:milp} bricht mit der Annahme beliebiger Teilbarkeit: Was, wenn man nur
ganze Maschinen kaufen kann? Wir werden sehen, dass Runden der LP-Lösung nicht nur ungenau,
sondern grundsätzlich falsch ist — und lernen Branch-and-Bound kennen.
sondern grundsätzlich falsch ist — und lernen Branch-and-Bound{idx:Branch-and-Bound} kennen.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound {#kap:milp}
# Kapitel: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound{idx:Branch-and-Bound} {#kap:milp}
> **📌 Kapitel auf einen Blick**
>
@ -7,10 +7,10 @@
> **falsch** ist, und lernen die Modellierungstricks, mit denen sich logische Regeln in
> lineare Ungleichungen übersetzen lassen.
>
> **Voraussetzungen:** {ref:kap:lp} (LP, Relaxation, Schranken).
> **Voraussetzungen:** {ref:kap:lp} (LP, Relaxation{idx:Relaxation}, Schranken).
>
> **Danach können Sie:** Fixkosten, Entweder-Oder, Wenn-Dann und Kardinalitätsgrenzen
> modellieren, ein MILP mit CP-SAT oder HiGHS lösen, den MIP-Gap als Zusage an den
> modellieren, ein MILP mit CP-SAT{idx:CP-SAT} oder HiGHS lösen, den MIP-Gap{idx:MIP-Gap} als Zusage an den
> Auftraggeber lesen — und ein Ergebnis erkennen, das nur deshalb gut aussieht, weil
> das Big-M zu groß gewählt war.
>
@ -68,7 +68,7 @@
> MILP 882.00 EUR Mengen [10. 0. 90.] Schalter [ 1. -0. 1. ]
> ```
**Sehen Sie sich die Schalter der ersten Zeile an: 0,143.** Die LP-Relaxation eröffnet
**Sehen Sie sich die Schalter der ersten Zeile an: 0,143.** Die LP-Relaxation{idx:LP-Relaxation} eröffnet
Lager Mitte zu 14,3 % — und zahlt dafür nur 14,3 % der Fixkosten. So etwas gibt es in der
Wirklichkeit nicht. Ein Lager ist offen oder zu.
@ -107,10 +107,10 @@ Nach diesem Kapitel können Sie …
Kardinalität).
4. … begründen, warum $M$ so klein wie möglich gewählt werden muss.
5. … ein Portfolioproblem mit Ordergebühren und Höchstzahl an Positionen lösen.
6. … **Incumbent, Schranke und MIP-Gap** unterscheiden und aus einem abgebrochenen
6. … **Incumbent{idx:Branch-and-Bound!Incumbent}, Schranke und MIP-Gap** unterscheiden und aus einem abgebrochenen
Solverlauf eine belastbare Aussage ableiten.
7. … alle Solver-Statusfälle explizit behandeln, statt `OPTIMAL` vorauszusetzen.
8. … einen **Warm-Start** setzen — und messen, ob er auf Ihrer Problemklasse überhaupt
8. … einen **Warm-Start**{idx:Warm-Start} setzen — und messen, ob er auf Ihrer Problemklasse überhaupt
etwas bringt.
---
@ -130,7 +130,7 @@ Der verlockende Gedanke lautet: *„Wir lösen das Problem kontinuierlich und ru
Eine verbreitete, aber unbelegte Behauptung dazu lautet, man verliere dadurch „oft
20–50 % des Gewinns“. Rechnen wir es nach, statt es zu behaupten.
![Links die Handrechnung von unten als Bild: Die Zielfunktion läuft parallel zur
![Links die Handrechnung von unten als Bild: Die Zielfunktion{idx:Zielfunktion} läuft parallel zur
Restriktion, also ist die ganze Kante optimal. Aufrunden auf $(1;1)$ führt aus dem zulässigen
Bereich heraus, Abrunden auf $(0;0)$ auf den Wert null. Rechts die Messung aus
`Runden_Gegenbeispiel.py` über je 200 Zufallsinstanzen. Erzeugt von
@ -384,7 +384,7 @@ also das, was der Solver sieht, bevor $y$ ganzzahlig wird. Erzeugt von
beiden blauen Punkte stimmen überein. Verschieden ist nur, was dazwischen erlaubt ist — und
rechts genügt bereits $y = 0{,}08$, um die volle Kapazität freizugeben.
### Muster 1 — Fixkosten / Aktivierungsschalter
### Muster 1 — Fixkosten / Aktivierungsschalter{idx:Aktivierungsschalter (Modellierungsmuster)}
*Regel:* Wird Maschine $j$ genutzt ($x_j > 0$), fallen Rüstkosten $F_j$ an.
@ -409,7 +409,7 @@ $$
* $y=1$: erste Bedingung aktiv, zweite durch $+M$ praktisch außer Kraft.
* $y=0$: umgekehrt.
### Muster 3 — Wenn-Dann (Implikation)
### Muster 3 — Wenn-Dann (Implikation{idx:Implikation (Modellierungsmuster)})
*Regel:* Wenn $y_1 = 1$, dann muss auch $y_2 = 1$ sein.
@ -467,7 +467,7 @@ Dieses Muster{idx:Semikontinuierliche Variable} brauchen wir gleich für die Min
---
## Beispiel: Das Rucksackproblem {#sec:milp-beispiel-das-rucksackproblem}
## Beispiel: Das Rucksackproblem{idx:Rucksackproblem} {#sec:milp-beispiel-das-rucksackproblem}
Bevor wir zum Portfolio kommen, das klassische Einstiegsproblem{idx:Rucksackproblem} — kurz, verständlich und
überall wiederzuerkennen.
@ -893,7 +893,7 @@ Abnahmepruefung: alle Nebenbedingungen geprueft und eingehalten.
> Kardinalitätsgrenze läge alles im Bestwert (13 %); die Restriktionen kosten 15 % des
> theoretischen Ertrags. Genau diese Zahl braucht man, wenn man mit dem Risikomanagement
> über die Sinnhaftigkeit einer Regel diskutiert: „Die Obergrenze von 40 % je Position
> kostet uns 1 950 € pro Jahr — ist uns die Diversifikation das wert?“ Das ist eine
> kostet uns 1 950 € pro Jahr — ist uns die Diversifikation{idx:Diversifikation} das wert?“ Das ist eine
> beantwortbare Frage; „wir sollten breiter streuen“ ist es nicht.
---
@ -1828,7 +1828,7 @@ funktioniert immer.
Entweder-Oder, Wenn-Dann, Kardinalität, semikontinuierlich.
* **$M$ so klein wie möglich** — nicht nur wegen der Laufzeit. Ein übergroßes $M$ kann
Binärvariablen bei $10^{-8}$ hängen lassen, wo sie dem Solver als 0 gelten: Anlagen
produzieren, ohne dass ihre Fixkosten je verbucht werden (*Trickle Flow*).
produzieren, ohne dass ihre Fixkosten je verbucht werden (*Trickle Flow*{idx:Trickle Flow}).
* **`OPTIMAL` ist nicht der Normalfall.** Werten Sie jeden Status aus und lesen Sie den
**MIP-Gap** — er ist eine Garantie („höchstens 2,3 % vom Optimum“), keine Schätzung. Der
Beweis der Optimalität kostet meist ein Vielfaches dessen, was das Finden der optimalen

View file

@ -1,4 +1,4 @@
# Kapitel: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung {#kap:cpsat}
# Kapitel: Constraint Programming mit CP-SAT{idx:CP-SAT} — Logik, Scheduling und Zuweisung {#kap:cpsat}
> **📌 Kapitel auf einen Blick**
>
@ -9,7 +9,7 @@
> **Voraussetzungen:** {ref:kap:milp} (Binärvariablen, logische Bedingungen).
>
> **Danach können Sie:** Zuweisungs-, Scheduling- und Reihenfolgeprobleme mit CP-SAT
> modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten steuern — und
> modellieren, globale Constraints einsetzen, weiche Ziele über Strafkosten{idx:Strafkosten} steuern — und
> alle fünf Solver-Antworten auseinanderhalten, statt nur auf `OPTIMAL` zu prüfen.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
@ -105,7 +105,7 @@ Nach diesem Kapitel können Sie …
7. … begründen, warum `num_workers = 1` für einen reproduzierbaren Lauf nötig ist und ein
fester `random_seed` allein nicht genügt — und was ein Test deshalb prüfen darf.
8. … begründen, warum ein Strafgewicht ein **Wechselkurs** ist, und welche Regeln deshalb
niemals in die Zielfunktion gehören.
niemals in die Zielfunktion{idx:Zielfunktion} gehören.
---
@ -118,7 +118,7 @@ Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen,
| --- | --- | --- |
| Variablen | reell oder ganzzahlig, mit Schranken | diskrete **Wertebereiche** (*domains*) |
| Bedingungen | lineare Ungleichungen | beliebige logische Relationen |
| Suchprinzip | Relaxation + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
| Suchprinzip | Relaxation{idx:Relaxation} + Verzweigen + Schranken | **Propagation** + Verzweigen + Konfliktlernen |
| Stärke | ökonomische Modelle, Kosten, Kapazitäten | Zuordnung, Reihenfolge, Zeitfenster, Regeln |
| Schwäche | viele logische Regeln → viele Big-M | reine Kostenoptimierung über Kontinuum |
@ -126,11 +126,11 @@ Der **CP-SAT-Solver**{idx:CP-SAT-Solver} von Google OR-Tools verbindet Constrain
**SAT-Techniken**{idx:SAT (Boolean Satisfiability)} (*Boolean Satisfiability*, Erfüllbarkeitsproblem der Aussagenlogik),
insbesondere **CDCL**{idx:CDCL} (*Conflict-Driven Clause Learning*, konfliktgetriebenes Klausellernen).
Er gehört zu den weltweit leistungsfähigsten Engines für Schichtpläne, Vertretungsplanung,
Projektterminierung und Job-Shop-Scheduling.
Projektterminierung und Job-Shop-Scheduling{idx:Job-Shop-Scheduling}.
### Was Propagation leistet
![CP-SAT – Propagation und Conflict Learning](bilder_04/kap06_cp_sat_propagation.svg)
![CP-SAT – Propagation und Conflict Learning{idx:CDCL!Conflict Learning}](bilder_04/kap06_cp_sat_propagation.svg)
> **✏️ Handrechnung: Propagation von Hand**
>
@ -246,7 +246,7 @@ if __name__ == "__main__":
---
## Globale Constraints — die Bausteine von CP-SAT {#sec:cpsat-globale-constraints-die-bausteine-von-cp}
## Globale Constraints{idx:Globale Constraints} — die Bausteine von CP-SAT {#sec:cpsat-globale-constraints-die-bausteine-von-cp}
CP-SAT stellt mächtige Makros{idx:Globale Constraints} bereit, die in MILP hunderte Big-M-Ungleichungen erfordern
würden. Jedes davon hat einen spezialisierten, hocheffizienten Propagator.
@ -604,9 +604,9 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
> CP-SAT-Konstrukte, die in MILP mehrere Big-M-Ungleichungen bräuchten. Die Spannweite
> $\max - \min$ zu minimieren ist ein Standardrezept für „gleichmäßig verteilen“.
>
> **3. Kostenzerlegung.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In
> **3. Kostenzerlegung{idx:Kostenzerlegung}.** Der Block „Woraus bestehen die Strafkosten?“ ist kein Beiwerk. In
> der Praxis ist die erste Frage jedes Anwenders: *„Warum bekomme **ich** die Stunde?“* Wer
> darauf keine Antwort hat, dessen System wird nicht benutzt ({ref:kap:praxisfallen}, *Explainable OR*).
> darauf keine Antwort hat, dessen System wird nicht benutzt ({ref:kap:praxisfallen}, *Explainable OR*{idx:Explainable OR}).
>
> **4. Statusauswertung ohne CP-SAT-Vokabular.** Nach `Solve()` steht nur eine einzige Zeile
> mit einer CP-SAT-Konstante darin:
@ -615,7 +615,7 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
> ```
> Danach heißen die Fälle `status.modellfehler` (die Regeln widersprechen sich — das Modell
> muss geändert werden) und `status.brauchbar` (es liegt etwas Ausführbares vor). Das ist
> **dieselbe** Fallunterscheidung wie bei HiGHS in {ref:kap:milp} und bei GLOP in
> **dieselbe** Fallunterscheidung wie bei HiGHS{idx:HiGHS} in {ref:kap:milp} und bei GLOP in
> {ref:kap:lp}; nur die eine Übersetzerzeile weiß, welcher Solver gerechnet hat. Wer den
> Auswertungscode direkt gegen `cp_model.INFEASIBLE` schreibt, kann ihn bei einem
> Solverwechsel wegwerfen — {ref:sec:praxisfallen-solverwechsel} führt genau diesen Wechsel
@ -625,7 +625,7 @@ Suchstatistik: 113 Verzweigungen, 3 Konflikte, 0.006 s
>
> * **Zu viele harte Regeln.** Jede zusätzliche harte Bedingung erhöht das Risiko von
> `INFEASIBLE`. Faustregel: Hart ist nur, was rechtlich oder physikalisch unmöglich ist.
> Alles andere wird weich mit hoher Strafe — dann liefert der Solver den „am wenigsten
> Alles andere wird weich{idx:Nebenbedingung!weich} mit hoher Strafe — dann liefert der Solver den „am wenigsten
> schlechten“ Plan statt einer Fehlermeldung.
> * **Strafgewichte willkürlich wählen.** Die Gewichte (50, 80, 30) definieren implizit eine
> Werteordnung: „Ein Freistundenloch ist so schlimm wie 1,6 Stunden Vorbelastung.“
@ -834,7 +834,7 @@ theoretischen Ideal entfernt sein können, noch bevor der Solver fertig war.
> **💡 Warum Intervallvariablen so wertvoll sind**
>
> Eine Intervallvariable bündelt drei Größen (Start, Dauer, Ende) und garantiert intern
> Eine Intervallvariable{idx:Intervallvariable} bündelt drei Größen (Start, Dauer, Ende) und garantiert intern
> `start + dauer == ende`. Der eigentliche Gewinn ist aber `AddNoOverlap`: Diese eine Zeile
> ersetzt für $k$ Gänge auf einer Maschine $\binom{k}{2}$ Entweder-Oder-Konstruktionen mit
> je zwei Big-M-Ungleichungen und einer Binärvariablen. Bei 10 Gängen sind das 45
@ -1123,7 +1123,7 @@ random_seed = 1 in JEDEM Lauf, 4 Wiederholungen je Arbeiterzahl.
nicht achtmal, sondern **gut zwölfmal** schneller als einer. Das sieht nach Messfehler aus, ist aber die
korrekte Beschreibung dessen, was CP-SAT tut: Es lässt **nicht** achtmal dieselbe Suche auf
verschiedenen Kernen laufen, sondern **verschiedene Strategien nebeneinander** — Suche mit
LP-Relaxation, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen
LP-Relaxation{idx:LP-Relaxation}, Suche mit anderen Verzweigungsregeln, lokale Suche —, und die teilen
einander ihre Schranken mit. Der zweite Arbeiter ist deshalb nicht „der zweite Kern“,
sondern ein anderes Verfahren, das zufällig auch rechnet.
@ -1146,7 +1146,7 @@ entscheidet die Uhr, und die ist bei jedem Lauf anders.
> **⚠️ `random_seed` allein macht nichts reproduzierbar**
> Ein häufiger Irrtum: „Ich habe doch einen Seed gesetzt.“ Für einen Zufallsgenerator
> stimmt das. Für einen **parallelen** Solver nicht — dort ist die zweite Zufallsquelle die
> Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter festnageln.
> Ausführungsreihenfolge, und die lässt sich nicht mit einem Parameter{idx:Parameter (Modell)} festnageln.
> Reproduzierbar wird der Lauf erst mit `num_workers = 1` **und** Seed. Wer nur eines von
> beidem setzt, hat einen Test, der gelegentlich grundlos rot wird.
@ -1485,7 +1485,7 @@ genau die Information, die zur Fehlersuche noetig waere.
>
> | Stelle | Was passiert | Warum es zählt |
> | --- | --- | --- |
> | `werte_aus()` | übersetzt Status in **Handlungsempfehlung** | Genau diese Funktion fehlt in den meisten Projekten. Sie ist kurz — und der Unterschied zwischen einem Prototyp und einem System, das nachts ohne Aufsicht läuft. |
> | `werte_aus()` | übersetzt Status in **Handlungsempfehlung** | Genau diese Funktion{idx:Konvexität!Funktion} fehlt in den meisten Projekten. Sie ist kurz — und der Unterschied zwischen einem Prototyp und einem System, das nachts ohne Aufsicht läuft. |
> | `hat_loesung = status in (OPTIMAL, FEASIBLE)` | die eigentliche Frage | Nicht „war der Solver erfolgreich?“, sondern „habe ich etwas in der Hand, das ich ausführen kann?“ |
> | `BestObjectiveBound()` | die bewiesene Schranke | Ohne sie ist `FEASIBLE` eine nutzlose Angabe. Mit ihr wird daraus „höchstens 1,27 % vom Optimum“. |
> | Läufe [2] und [3] | 0,5 s gegen 10 s | Der Gap fällt von 1,27 % auf 0,14 %. Zwanzigfache Rechenzeit für gut ein Prozent — die Entscheidung, ob sich das lohnt, trifft die Anwendung, nicht der Solver. |
@ -1525,9 +1525,9 @@ Erweitern Sie das Vertretungssystem um:
(b) Herr Bauer und Frau Koch sollen nicht beide am selben Tag eingesetzt werden.
(c) Wer Slot 1 übernimmt, soll bevorzugt auch Slot 2 bekommen (Belohnung statt Strafe).
**Aufgabe ⭐⭐ — Infeasibility erzeugen und diagnostizieren.**
**Aufgabe ⭐⭐ — Infeasibility{idx:Infeasibility} erzeugen und diagnostizieren.**
Setzen Sie `MAX_VERTRETUNGEN = 0`. Was meldet das Programm? Bauen Sie anschließend eine
**Schlupfvariable** je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie
**Schlupfvariable**{idx:Schlupfvariable} je Slot ein („Stunde fällt aus“, Strafe 10 000) und beobachten Sie, wie
der Solver statt einer Fehlermeldung einen Notfallplan liefert. Welche Stunde lässt er
ausfallen und warum?
@ -1785,7 +1785,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
> Der Plan ist mit dem vorhandenen Personal nicht zulässig zu machen.
> * **Gewichte in verschiedenen Einheiten mischen.** Euro neben „Unzufriedenheitspunkten“
> neben Minuten ergibt eine Zielfunktion, die niemand interpretieren kann — und deren
> Konditionszahl obendrein leidet ({ref:sec:fundament-kondition}).
> Konditionszahl{idx:Konditionszahl} obendrein leidet ({ref:sec:fundament-kondition}).
> * **Nur ein Gewicht ausprobieren.** Ein einziger Lauf sagt nichts darüber, ob Sie nahe an
> einem Kipppunkt stehen. Zwischen den Gewichten 5 und 10 ändert sich der Plan oben
> erheblich.
@ -1830,7 +1830,7 @@ Nebenbedingungen. Alles, was einen Preis hat, wird irgendwann gekauft.
1. Was macht Constraint Propagation, und warum ist sie effizienter als Ausprobieren?
2. Welchen Vorteil hat `AddAllDifferent(x)` gegenüber $\binom{n}{2}$ Einzelbedingungen?
3. Was ist eine Intervallvariable, und welche Bedingung garantiert sie automatisch?
4. Warum sollten möglichst wenige Bedingungen hart formuliert werden?
4. Warum sollten möglichst wenige Bedingungen hart{idx:Nebenbedingung!hart} formuliert werden?
5. Warum ist die Aufschlüsselung der Strafkosten in der Praxis wichtiger als der Zielwert
6. Sie setzen `random_seed = 1`, bekommen aber trotzdem bei jedem Lauf einen anderen Plan.
Woran liegt das, und was hilft?

View file

@ -85,11 +85,11 @@ mehr.
> nacheinander. Bei vier Monteuren kostet die gierige Regel 8 %; bei vierzig kostet sie
> regelmäßig ein Vielfaches.
**Warum funktioniert das?** Weil das Zuordnungsproblem eine besondere Struktur hat: Seine
**Warum funktioniert das?** Weil das Zuordnungsproblem{idx:Zuordnungsproblem (1:1)} eine besondere Struktur hat: Seine
Nebenbedingungsmatrix ist **total unimodular**. Das bedeutet — wir kommen in
{ref:sec:graphen-bipartites-matching-das-zuordnungsproblem} darauf zurück —, dass die
LP-Relaxation von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder
Branch-and-Bound noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so
LP-Relaxation{idx:LP-Relaxation} von ganz allein ganzzahlige Lösungen liefert. Man braucht hier also weder
Branch-and-Bound{idx:Branch-and-Bound} noch Binärvariablen. Dieselbe Eigenschaft macht auch Flussprobleme so
angenehm lösbar, und damit beginnt das Kapitel.
---
@ -102,7 +102,7 @@ Nach diesem Kapitel können Sie …
2. … den **Flusserhaltungssatz**{idx:Flusserhaltung} aufstellen und seine Bedeutung erklären.
3. … begründen, warum Zuordnungsprobleme **ohne** Ganzzahligkeitsbedingung ganzzahlig lösbar
sind (totale Unimodularität{idx:Totale Unimodularität}).
4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools lösen.
4. … ein Vehicle Routing Problem mit Kapazitäten und Zeitfenstern mit OR-Tools{idx:OR-Tools} lösen.
5. … einschätzen, wann ein spezialisierter Algorithmus einem allgemeinen MILP überlegen ist.
6. … begründen, warum eine gierige Zuordnung systematisch schlechter ist als eine
gemeinsame Optimierung.
@ -128,7 +128,7 @@ mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverte
Route über den Umschlag läuft voll (dick, amber), die Kante Werk B → Umschlag bleibt
ungenutzt (gestrichelt) — und trotzdem muss die teure Direktkante Werk B → Kunde 2 für 6 €
bedient werden, weil der Umschlagweg dorthin schon ausgelastet ist. An den Knoten stehen die
Dualwerte der Flusserhaltung. Erzeugt von
Dualwerte der Flusserhaltung{idx:Flusserhaltung}. Erzeugt von
`bilder_04/erzeuge_min_cost_flow.py`.](bilder_04/kap_graphen_min_cost_flow.svg)
Sei $x_{ij} \ge 0$ der Fluss über Kante $(i,j)$, $c_{ij}$ die Kosten je Einheit und
@ -307,7 +307,7 @@ schon bei zehn Knoten den Überblick verliert.
---
## Bipartites Matching: das Zuordnungsproblem {#sec:graphen-bipartites-matching-das-zuordnungsproblem}
## Bipartites Matching{idx:Bipartites Matching}: das Zuordnungsproblem {#sec:graphen-bipartites-matching-das-zuordnungsproblem}
Wenn $N$ Aufgaben auf $N$ Ressourcen **eins zu eins** verteilt werden — Orders auf Broker,
Schichten auf Mitarbeitende, Aufträge auf Maschinen — spricht man von **bipartitem
@ -319,9 +319,9 @@ $$
\sum_j x_{ij} = 1\ \forall i,\qquad \sum_i x_{ij} = 1\ \forall j,\qquad x_{ij}\ge0
$$
### Der Satz von Birkhoff und von Neumann
### Der Satz von Birkhoff und von Neumann{idx:Satz von Birkhoff und von Neumann}
> **Satz.** Die Extrempunkte der Menge aller doppelt-stochastischen Matrizen (alle
> **Satz.** Die Extrempunkte der Menge{idx:Konvexität!Menge} aller doppelt-stochastischen Matrizen (alle
> Zeilensummen $= 1$, alle Spaltensummen $= 1$, $x_{ij} \ge 0$) sind **genau** die
> Permutationsmatrizen (alle $x_{ij} \in \{0,1\}$).
@ -353,8 +353,8 @@ selbst“ ganzzahlig.
>
> **Und die Warnung dazu:** Diese Eigenschaft ist zerbrechlich. Eine einzige zusätzliche
> Nebenbedingung, die nicht in das Schema passt — „höchstens drei Fahrzeuge insgesamt“,
> eine Fixkostenkopplung, eine Mindestabnahmemenge — zerstört die totale Unimodularität.
> Dann liefert die Relaxation wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie
> eine Fixkostenkopplung, eine Mindestabnahmemenge{idx:Mindestabnahmemenge} — zerstört die totale Unimodularität.
> Dann liefert die Relaxation{idx:Relaxation} wieder Brüche, und Sie brauchen doch ein MILP. Prüfen Sie
> das, bevor Sie sich auf die Struktur verlassen.
```python
@ -539,9 +539,9 @@ $$
> **💡 In der Praxis: nicht selbst modellieren**
> Die MTZ-Formulierung ist didaktisch wertvoll, aber für reale Instanzen zu schwach — die
> LP-Relaxation ist sehr locker, und Branch-and-Bound braucht sehr lange. Professionelle
> Solver verwenden stattdessen dynamisch erzeugte Subtour-Eliminationsschnitte oder, wie
> Solver verwenden stattdessen dynamisch erzeugte Subtour{idx:Subtour}-Eliminationsschnitte oder, wie
> OR-Tools, **spezialisierte Metaheuristiken**. Nutzen Sie für Routing die
> **Routing-Bibliothek**, nicht ein selbstgebautes MILP.
> **Routing-Bibliothek**{idx:Routing-Bibliothek}, nicht ein selbstgebautes MILP.
### Praxisbeispiel: Flotten-Routing
@ -722,7 +722,7 @@ if __name__ == "__main__":
> verschiebt sich der ganze Plan.
> * **Zu enge Zeitfenster ohne Vorabprüfung.** Ist ein Kunde in seinem Fenster physisch nicht
> erreichbar, meldet OR-Tools nur „keine Lösung“ — ohne zu sagen, welcher Kunde schuld ist.
> Die Funktion `pruefe_daten()` fängt genau das ab.
> Die Funktion{idx:Konvexität!Funktion} `pruefe_daten()` fängt genau das ab.
> * **Ergebnisse als exakt betrachten.** Die Routing-Bibliothek nutzt **Metaheuristiken**.
> Ein längeres Zeitlimit kann eine bessere Lösung liefern; „optimal“ wird hier in der Regel
> nicht bewiesen. Für die Praxis genügt das fast immer — man sollte es aber wissen.
@ -1032,7 +1032,7 @@ plausibel aussieht.
> * **Die Prüfung aus denselben Bausteinen bauen wie das Modell.** Wer die Ladung mit
> `loesung.Value(ladungs_dimension.CumulVar(...))` prüft, fragt das Modell, ob es sich an
> sich selbst hält. Rechnen Sie stattdessen aus der **ausgegebenen Tour** neu nach.
> * **Zwei Läufe nur an der Zielfunktion vergleichen.** 326 gegen 572 km sagt nichts,
> * **Zwei Läufe nur an der Zielfunktion{idx:Zielfunktion} vergleichen.** 326 gegen 572 km sagt nichts,
> solange nicht feststeht, dass beide Pläne überhaupt fahrbar sind.
> * **Unbenutzte Fahrzeuge übersehen.** Drei Fahrzeuge, die im Depot stehen, während eines
> alles fährt, sind fast immer ein Zeichen für eine fehlende Beschränkung.
@ -1086,7 +1086,7 @@ plausibel aussieht.
* **Graphen** sind die natürliche Sprache für Transport-, Zuordnungs- und Routenprobleme.
* **Flusserhaltung** ist die Kirchhoff-Regel des Operations Research: Was hineingeht, kommt
heraus — abzüglich des Knotensaldos.
* **Totale Unimodularität** macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer
* **Totale Unimodularität**{idx:Totale Unimodularität} macht Fluss- und Zuordnungsprobleme „von selbst“ ganzzahlig. Wer
hier `integrality` setzt, verschenkt Laufzeit ohne Gegenwert.
* **Spezialisierte Algorithmen schlagen allgemeine Solver** deutlich: Der Ungarische
Algorithmus{idx:Ungarischer Algorithmus} löst in $O(n^3)$, wofür ein MILP-Solver Branch-and-Bound bräuchte.

View file

@ -7,10 +7,10 @@
> und eine Entscheidung muss trotzdem heute fallen.
>
> **Voraussetzungen:** {ref:kap:milp} und {ref:kap:cpsat}, insbesondere
> {ref:sec:milp-gap} (MIP-Gap und Schranke).
> {ref:sec:milp-gap} (MIP-Gap{idx:MIP-Gap} und Schranke).
>
> **Danach können Sie:** eine lokale Suche mit ihrer entscheidenden Zutat — der
> Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing
> Kostenänderung in O(1) — selbst schreiben, die Temperatur eines Simulated Annealing{idx:Simulated Annealing}
> ausmessen statt raten, den Umschlagpunkt zwischen exakt und heuristisch für Ihr eigenes
> Problem bestimmen, und mit Large Neighborhood Search beides kombinieren.
>
@ -99,7 +99,7 @@ Nach diesem Kapitel können Sie …
1. … erklären, warum eine lokale Suche ohne **Kostenänderung in O(1)** praktisch wertlos ist.
2. … Simulated Annealing implementieren und seine Starttemperatur **an der Zuggröße**
kalibrieren statt am Zielfunktionswert.
3. … den Beitrag der Metaheuristik gegen die einfachere Alternative abgrenzen — reines
3. … den Beitrag der Metaheuristik{idx:Metaheuristik} gegen die einfachere Alternative abgrenzen — reines
Bergsteigen — statt ihn ihr gutzuschreiben.
4. … den **Umschlagpunkt** bestimmen, ab dem eine Metaheuristik den exakten Solver schlägt,
und begründen, warum diese Zahl problemspezifisch ist.
@ -136,7 +136,7 @@ $$
Wer die Aufgabe als Graph liest, erkennt sie wieder: Es ist ein **asymmetrisches
Rundreiseproblem** ({ref:kap:graphen}) mit offenem Ende. Das ist eine gute und eine
schlechte Nachricht. Gut, weil damit alles bekannt ist, was man über das Problem wissen
kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
kann. Schlecht, weil dazu gehört, dass es NP-schwer{idx:NP-schwer} ist.
> **📖 Definition: Metaheuristik**{idx:Metaheuristik}
>
@ -151,7 +151,7 @@ kann. Schlecht, weil dazu gehört, dass es NP-schwer ist.
### Die Instanz
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei
Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit{idx:Rüstzeit} setzt sich aus zwei
Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und
einem Zuschlag für den Wechsel von Dunkel nach Hell.
@ -895,7 +895,7 @@ if __name__ == "__main__":
print("=" * 88)
```
**Erwartete Ausgabe** (die CP-SAT-Spalten sind hardwareabhängig, siehe Programmkopf):
**Erwartete Ausgabe** (die CP-SAT{idx:CP-SAT}-Spalten sind hardwareabhängig, siehe Programmkopf):
```
========================================================================================
@ -1428,7 +1428,7 @@ Starttemperatur würde eine Verschlechterung von genau 70 Minuten in 37 % der F
angenommen? Vergleichen Sie das mit den Temperaturen in der Tabelle.
**Aufgabe ⭐⭐ — Die teure Bewertung.**
Ersetzen Sie in `Simulated_Annealing.py` die Funktion `delta_verschieben` durch eine
Ersetzen Sie in `Simulated_Annealing.py` die Funktion{idx:Konvexität!Funktion} `delta_verschieben` durch eine
Variante, die den Zug ausführt und `gesamtruestzeit()` neu berechnet. Wie viele Züge schafft
das Programm dann in derselben Zeit, und wie verändert sich das Ergebnis bei gleichem
Zeitbudget?
@ -1510,7 +1510,7 @@ liegt es an der Strategie oder an der Zahl der Runden, die sie schafft?
* Eine **Faustregel** ist selten dumm, aber immer kurzsichtig: Sie bewertet einen Schritt
danach, was er kostet, nicht danach, was er übrig lässt.
* **Lokale Suche** steht und fällt mit der Kostenänderung in $O(1)$. Wer den Zug über die
volle Zielfunktion bewertet, misst am Ende seine Implementierung statt des Verfahrens.
volle Zielfunktion{idx:Zielfunktion} bewertet, misst am Ende seine Implementierung statt des Verfahrens.
* **Simulated Annealing** nimmt Verschlechterungen mit $e^{-\Delta/T}$ an. $T$ und $\Delta$
haben dieselbe Einheit — die Temperatur gehört an der **Zuggröße** kalibriert, nicht am
Zielfunktionswert. Die verbreitete Regel „20 bis 50 % Annahmequote“ war auf dieser Aufgabe

View file

@ -73,7 +73,7 @@ je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschw
Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.
> **🎯 Merksatz**
> Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die
> Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion{idx:Zielfunktion}. Es ist die
> Frage, **worüber** die Variablen laufen.
**Warum funktioniert das?** Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei
@ -98,7 +98,7 @@ Nach diesem Kapitel können Sie …
## Warum das naheliegende Modell scheitert {#sec:dekomposition-symmetrie}
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable
Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable{idx:Binärvariable}
$z_{jr}$ für „Stück $j$ liegt auf Rolle $r$", dazu $y_r$ für „Rolle $r$ wird benutzt".
Zwei Dinge gehen dabei schief.
@ -108,14 +108,14 @@ Binärvariablen — unangenehm, aber allein noch kein Hindernis.
**Die Symmetrie.** Das ist das eigentliche Problem. Alle Mutterrollen sind **gleich**. Jede
Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47,
entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht
entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound{idx:Branch-and-Bound} weiß das nicht
und arbeitet sie einzeln ab.
> **⚠️ Woran man ein Symmetrieproblem erkennt**
>
> Der Suchbaum wächst, aber die **Schranke bewegt sich nicht.** Im Protokoll aus
> {ref:sec:milp-gap} sieht das so aus: Die Zahl der Knoten steigt in die Hunderttausende,
> der Incumbent verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
> der Incumbent{idx:Branch-and-Bound!Incumbent} verbessert sich hin und wieder, und der Dual Bound steht praktisch still.
>
> Das ist ein anderes Bild als „das Problem ist einfach zu groß". Bei einem großen, aber
> unsymmetrischen Problem nähern sich beide Werte einander an, nur langsam.
@ -134,7 +134,7 @@ $$
>
> | Mathematik | Alltagssprache |
> | --- | --- |
> | $P$ | die Menge aller zulässigen Schnittmuster |
> | $P$ | die Menge{idx:Konvexität!Menge} aller zulässigen Schnittmuster |
> | $a_{ip}$ | „Wie viele Stücke der Breite $i$ liefert Muster $p$?" |
> | $x_p$ | „Wie oft schneide ich Muster $p$?" — die einzige Entscheidung |
> | $d_i$ | die bestellte Stückzahl der Breite $i$ |
@ -156,7 +156,7 @@ Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:
> Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.
Die Frage „lohnt sich noch ein Muster?" beantworten die **Dualwerte** des Master-LPs. Der
Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der Schattenpreis aus
Dualwert $\pi_i$ zur Bedarfszeile $i$ ist genau der Schattenpreis{idx:Schattenpreis} aus
{ref:sec:lp-dualitaet-und-schattenpreise}: Er sagt, wie viele Rollen ein zusätzliches Stück
der Breite $i$ kostet.
@ -189,7 +189,7 @@ $$
> Mustern optimal — auch über den nie erzeugten.
**Und wie findet man das beste neue Muster?** Man sucht die Zusammenstellung von Stücken mit
dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **Rucksackproblem**:
dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein **Rucksackproblem**{idx:Rucksackproblem}:
| Rucksack ({ref:kap:milp}) | hier |
| --- | --- |

View file

@ -10,7 +10,7 @@ ist: Welches nehme ich, und was bekomme ich dafür?
| --- | --- | --- | --- | --- |
| **LP** | teilbare Mengen, lineare Zusammenhänge | beweisbares Optimum **plus Schattenpreise** | sobald etwas ganzzahlig sein muss | {ref:kap:lp} |
| **MILP** | Ja/Nein-Entscheidungen, Fixkosten, Logik | beweisbares Optimum, Gap als Fortschrittsmaß | Laufzeit wächst mit der Zahl der Binärvariablen | {ref:kap:milp} |
| **CP-SAT** | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | {ref:kap:cpsat} |
| **CP-SAT**{idx:CP-SAT} | Zuweisung, Reihenfolge, Kalender, harte Logik | beweisbares Optimum, sehr ausdrucksstarke Bedingungen | keine Schattenpreise, keine stetigen Größen | {ref:kap:cpsat} |
| **Graphenalgorithmen** | Flüsse, Zuordnung, Touren | oft **polynomiell** statt exponentiell | nur, wenn die Struktur wirklich passt | {ref:kap:graphen} |
| **Metaheuristiken** | wenn der exakte Solver aussteigt | eine gute Lösung in **fester Zeit** | keine Garantie, kein Beweis, keine Schranke | {ref:kap:metaheuristiken} |
| **Spaltengenerierung** | Modelle mit astronomisch vielen Variablen | beweisbares Optimum über nie erzeugte Spalten | lohnt nur bei bestimmter Instanzstruktur | {ref:kap:dekomposition} |
@ -23,7 +23,7 @@ wissen, wo es aufhört — nicht, wofür es gedacht ist.
| Behauptung | Gemessen | Wo |
| --- | --- | --- |
| „Der exakte Solver ist immer besser." | Bei 500 Aufträgen liefert CP-SAT eine Lösung, die **5,2 % schlechter** ist als die Faustregel eines Meisters | {ref:kap:metaheuristiken} |
| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus LNS **2 289** Minuten Rüstzeit; untere Schranke 1 768 | {ref:kap:metaheuristiken} |
| „Metaheuristiken sind ungenau." | Faustregel 2 497 → Annealing 2 343 → Annealing plus LNS **2 289** Minuten Rüstzeit{idx:Rüstzeit}; untere Schranke 1 768 | {ref:kap:metaheuristiken} |
| „Man muss alle Variablen aufschreiben." | 28 erzeugte Muster von **46 408** möglichen — und die Lösung ist beweisbar optimal | {ref:kap:dekomposition} |
| „Dekomposition lohnt sich immer." | Bei drei Stücken je Rolle **11 %** Ersparnis, bei sechs **nichts** | {ref:kap:dekomposition} |
| „Mit Seed ist der Lauf reproduzierbar." | Schon **zwei** Arbeiter liefern bei identischem Seed drei verschiedene Pläne zum selben Zielwert | {ref:sec:cpsat-parallele-suche} |
@ -34,7 +34,7 @@ wissen, wo es aufhört — nicht, wofür es gedacht ist.
weit danebenliegen, und gerundet sogar unzulässig werden.
2. **Ein zu großes Big-M.** Es macht das Modell nicht falsch, sondern die Suche langsam und
die Schranken wertlos. So klein wie zulässig, nie „sicherheitshalber groß".
3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine Metaheuristik den exakten
3. **Zu früh heuristisch werden.** Der Umschlagpunkt, ab dem eine Metaheuristik{idx:Metaheuristik} den exakten
Solver schlägt, ist eine Eigenschaft **des Problems** und lässt sich messen. Wer ihn
nicht misst, verzichtet auf Optimalitätsgarantien, die er hätte haben können.

View file

@ -12,7 +12,7 @@ brauchen, hängt davon ab, **welche** davon bei Ihnen vorliegt:
> **🎯 Merksatz zum Diagramm**
> Die erste Frage trennt zwei Welten, die man nicht vermischen darf. Ist die
> **Nichtlinearität** das Problem, geht es um Konvexität — und damit darum, ob Sie eine
> **Nichtlinearität** das Problem, geht es um Konvexität{idx:Konvexität} — und damit darum, ob Sie eine
> Optimalitätsgarantie bekommen oder nur ein lokales Optimum. Ist die **Unsicherheit** das
> Problem, geht es um etwas ganz anderes: Nicht der Solver ist die Schwierigkeit, sondern
> die Frage, was „optimal“ überhaupt heißen soll, wenn man die Daten nicht kennt.
@ -33,15 +33,15 @@ insgesamt leistet, fasst die Synthese an seinem Ende zusammen.
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Risiko ist quadratisch. Sobald Varianz ins Spiel kommt, verlässt man
> die Welt der Polyeder. Dieses Kapitel liefert das mathematische Fundament für die gesamte
> die Welt der Polyeder{idx:Polyeder}. Dieses Kapitel liefert das mathematische Fundament für die gesamte
> Portfoliooptimierung in {ref:teil:anwendungen}.
>
> **Voraussetzungen:** {ref:kap:fundament} (Konvexität, Eigenwerte), {ref:kap:lp} (Dualität,
> Schattenpreise). Gradienten werden in {ref:sec:qp-nlp-lernziele} wiederholt.
>
> **Danach können Sie:** Ein quadratisches Programm aufstellen, die KKT-Bedingungen
> anwenden, den Zusammenhang zwischen Lagrange-Multiplikator und Schattenpreis erklären,
> eine gültige Kovarianzmatrix konstruieren — und mit nicht-konvexen Problemen umgehen,
> anwenden, den Zusammenhang zwischen Lagrange-Multiplikator{idx:Lagrange-Multiplikator} und Schattenpreis erklären,
> eine gültige Kovarianzmatrix{idx:Kovarianzmatrix} konstruieren — und mit nicht-konvexen Problemen umgehen,
> ohne ein lokales Optimum für das Optimum zu halten.
>
> **Zeitbedarf:** ca. 6,5 Stunden.
@ -120,8 +120,8 @@ $$
Der letzte Term ist der entscheidende: Bei negativer Korrelation ist er **negativ** und
zieht das Gesamtrisiko herunter. Deshalb verlässt man mit Risiko die Welt der Polyeder aus
{ref:teil:kernverfahren} — die Zielfunktion ist kein Skalarprodukt mehr, sondern eine
quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY das trotzdem mit
{ref:teil:kernverfahren} — die Zielfunktion{idx:Zielfunktion} ist kein Skalarprodukt{idx:Skalarprodukt} mehr, sondern eine
quadratische Form. Was das für die Lösbarkeit bedeutet und warum CVXPY{idx:CVXPY} das trotzdem mit
Optimalitätsgarantie löst, klärt der Rest des Kapitels.
> **🎯 Merksatz**
@ -139,7 +139,7 @@ Nach diesem Kapitel können Sie …
1. … ein **quadratisches Programm (QP)** aufstellen und von einem allgemeinen nichtlinearen
Programm abgrenzen.
2. … die **KKT-Bedingungen** herleiten und auf ein Optimierungsproblem anwenden.
3. … den Zusammenhang zwischen **Lagrange-Multiplikator** und **Schattenpreis** erklären.
3. … den Zusammenhang zwischen **Lagrange-Multiplikator** und **Schattenpreis**{idx:Schattenpreis} erklären.
4. … zwischen **konvex**{idx:Konvexität} und **streng konvex**{idx:Konvexität!streng konvex}
unterscheiden und eine gültige, positiv semidefinite **Kovarianzmatrix**{idx:Kovarianzmatrix}
konstruieren.
@ -204,11 +204,11 @@ Minimum.“* Korrekt gilt:
| Voraussetzung | Bedeutung | Folgerung |
| --- | --- | --- |
| $\mathbf{P} \succeq 0$ — **positiv semidefinit**, alle Eigenwerte $\lambda_i \ge 0$ | Die Funktion ist eine „Schüssel“, die eine flache Rinne haben darf | Problem ist **konvex**. Jedes lokale Minimum ist global. Es kann aber **mehrere Minimalstellen** geben — mit demselben Zielwert |
| $\mathbf{P} \succeq 0$ — **positiv semidefinit**, alle Eigenwerte $\lambda_i \ge 0$ | Die Funktion{idx:Konvexität!Funktion} ist eine „Schüssel“, die eine flache Rinne haben darf | Problem ist **konvex**. Jedes lokale Minimum ist global. Es kann aber **mehrere Minimalstellen** geben — mit demselben Zielwert |
| $\mathbf{P} \succ 0$ — **positiv definit**, alle $\lambda_i > 0$ | Echte Schüssel ohne flache Richtung | Problem ist **streng konvex**. Die Minimalstelle ist **eindeutig** |
| $\mathbf{P}$ hat einen negativen Eigenwert | Sattel oder Rinne nach unten | Problem ist **nicht konvex**. Lokale Optima möglich, keine Garantie |
**Warum das praktisch zählt:** Eine Stichproben-Kovarianzmatrix mit mehr Titeln als
**Warum das praktisch zählt:** Eine Stichproben-Kovarianzmatrix{idx:Stichproben-Kovarianzmatrix} mit mehr Titeln als
Beobachtungen ($N > T$) ist **singulär** — also nur semidefinit. Dann gibt es unendlich viele
Portfolios mit exakt demselben minimalen Risiko, und der Solver liefert eines davon,
scheinbar willkürlich. Kleine Datenänderungen führen zu völlig anderen Gewichten. Genau
@ -547,7 +547,7 @@ if __name__ == "__main__":
## Nichtlineare Optimierung mit `scipy.optimize.minimize` {#sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize}
Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen,
Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **SLSQP**
Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie **SLSQP**{idx:SLSQP}
(*Sequential Least Squares Programming*).
> **⚠️ Der entscheidende Unterschied zu CVXPY**
@ -564,7 +564,7 @@ Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzl
$$
\min_{\mathbf{w}}\quad \underbrace{\tfrac12\mathbf{w}^\top\boldsymbol{\Sigma}\mathbf{w}}_{\text{Risiko}}
\;-\;\underbrace{\alpha\,\boldsymbol{\mu}^\top\mathbf{w}}_{\text{Ertrag}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation}}
\;-\;\underbrace{\beta\Big(-\textstyle\sum_i w_i\ln w_i\Big)}_{\text{Diversifikation{idx:Diversifikation}}}
$$
> **📐 Formel-Lesehilfe zur Entropie**
@ -575,7 +575,7 @@ $$
> **Ohne Formel gesagt:** Die Entropie misst, wie „breit verteilt“ das Portfolio ist. Weil
> sie mit einem Minuszeichen in die zu minimierende Zielfunktion eingeht, wird breite
> Streuung **belohnt** — und zwar mathematisch glatt, ohne harte Obergrenzen. Der Vorteil
> gegenüber einer Schranke wie $w_i \le 0{,}2$: Der Übergang ist weich, das Ergebnis
> gegenüber einer Schranke wie $w_i \le 0{,}2$: Der Übergang ist weich{idx:Nebenbedingung!weich}, das Ergebnis
> reagiert nicht sprunghaft auf kleine Datenänderungen.
```python
@ -789,7 +789,7 @@ unverändert gelassen. **Der weiche Term verteilt, die harte Schranke kappt.**
---
## Jenseits der Konvexität: lokale Optima, Multistart und MINLP {#sec:qp-nlp-nichtkonvex}
## Jenseits der Konvexität: lokale Optima, Multistart und MINLP{idx:MINLP} {#sec:qp-nlp-nichtkonvex}
Alles bisher in diesem Kapitel stand unter einer Bedingung: **Konvexität**. Sie ist der
Grund, warum CVXPY eine Optimalitätsgarantie geben kann und warum die KKT-Bedingungen nicht
@ -807,7 +807,7 @@ Effekten:
| --- | --- | --- |
| **Mengenrabatte** | Stückpreis fällt mit der Bestellmenge | Kostenfunktion wird konkav — Sprünge nach unten belohnen große Lose |
| **Skaleneffekte** | Stückkosten sinken mit der Losgröße | dasselbe Muster in der Produktion |
| **Produkte von Variablen** | „Menge mal Preis“, wenn beide entschieden werden | $x \cdot y$ ist weder konvex noch konkav |
| **Produkte von Variablen** | „Menge{idx:Konvexität!Menge} mal Preis“, wenn beide entschieden werden | $x \cdot y$ ist weder konvex noch konkav |
| **Ja/Nein mal Menge** | Anlage läuft *und* wie stark | ganzzahlig + nichtlinear = MINLP |
| **Verhältnisse** | Auslastungsgrad, Rendite je eingesetztem Euro | Quotienten sind selten konvex |
@ -1124,9 +1124,9 @@ schwierigste der in diesem Buch behandelten Klassen. Dafür gibt es eigene Werkz
| Werkzeug | Art | Was es leistet |
| --- | --- | --- |
| **Ipopt** (über `cyipopt` oder Pyomo) | lokaler NLP-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
| **Ipopt** (über `cyipopt` oder Pyomo{idx:Pyomo}) | lokaler NLP-Solver | Innere-Punkte-Verfahren für große, glatte NLPs mit tausenden Variablen — deutlich leistungsfähiger als `scipy`, aber ebenfalls **nur lokal** |
| **Bonmin**, **Couenne** | MINLP | Bonmin lokal, Couenne mit globaler Garantie für viele Klassen |
| **SCIP**, **BARON**, **Gurobi** | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
| **SCIP**{idx:SCIP}, **BARON**, **Gurobi**{idx:Gurobi} | global | Beweisbar globales Optimum, mit entsprechendem Rechenaufwand |
| **Pyomo** ({ref:sec:oekosystem-pyomo-linopy}) | Modellierungsschicht | Bindet alle oben genannten an, ohne dass das Modell umgeschrieben werden muss |
> **⚠️ Zu diesem Abschnitt gehört kein lauffähiges Programm.** Ipopt, Bonmin und Couenne
@ -1200,7 +1200,7 @@ korrelieren?)
**Aufgabe ⭐⭐⭐ — Effekt der Gewichtung untersuchen.**
Variieren Sie in `Entropie_Maximierte_Allokation.py` systematisch $\alpha \in
\{0{,}1;\ 0{,}5;\ 1;\ 5\}$ und $\beta \in \{0;\ 0{,}005;\ 0{,}015;\ 0{,}05\}$. Stellen Sie
für jede Kombination Rendite, Volatilität und Entropie in einer Tabelle dar.
für jede Kombination Rendite, Volatilität{idx:Volatilität} und Entropie in einer Tabelle dar.
(a) Wie verändert $\beta$ die Konzentration im Krypto-Titel?
(b) Ab welchem $\beta$ nähert sich die Lösung der Gleichgewichtung?
(c) Was ist Ihre Empfehlung — und mit welcher Begründung würden Sie sie einem
@ -1349,7 +1349,7 @@ groß ist der Unterschied zwischen bestem und schlechtestem Ergebnis? Kann die
Nachkommastelle. „Bester Wert aus 200 Startpunkten, Spanne bis …“ ist belastbar, ein
einzelner Wert ist es nicht.
**Ausblick.** {ref:kap:unsicherheit} gibt die Annahme auf, dass die Parameter überhaupt bekannt sind. Wir
**Ausblick.** {ref:kap:unsicherheit} gibt die Annahme auf, dass die Parameter{idx:Parameter (Modell)} überhaupt bekannt sind. Wir
lernen, mit Szenarien, Erwartungswerten und Worst Cases umzugehen.
---

View file

@ -2,7 +2,7 @@
> **📌 Kapitel auf einen Blick**
>
> **Worum geht es?** Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage,
> **Worum geht es?** Bisher waren alle Parameter{idx:Parameter (Modell)} bekannt. In der Realität sind Nachfrage,
> Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen —
> und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.
>
@ -114,8 +114,8 @@ Nach diesem Kapitel können Sie …
1. … den **Fluch des Durchschnitts**{idx:Fluch des Durchschnitts} (*Flaw of Averages*) an einem Beispiel erklären.
2. … ein **zweistufiges stochastisches Programm** mit Recourse formulieren.
3. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
4. … ein **robustes** Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint
4. … ein **robustes** Modell mit Unsicherheitsmenge{idx:Unsicherheitsmenge} aufstellen und seinen Preis beziffern.
5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint{idx:Chance Constraint}
schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis
eines Prozentpunkts Sicherheit beziffern.
6. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
@ -274,9 +274,9 @@ Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.
![Ansätze für Optimierung unter Unsicherheit](bilder_04/kap09_unsicherheit_ansaetze.svg)
| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | **Chance Constraints** |
| | **Monte-Carlo** | **Stochastische Programmierung**{idx:Stochastische Programmierung} | **Robuste Optimierung**{idx:Robuste Optimierung} | **Chance Constraints** |
| --- | --- | --- | --- | --- |
| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | Verteilung **oder** Szenarien |
| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge**{idx:Konvexität!Menge} möglicher Werte | Verteilung **oder** Szenarien |
| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | Kosten bei **zugesicherter Quote** |
| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | „Was hält in 95 % der Fälle?“ |
| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | die Zusage steht **im Modell** |
@ -290,7 +290,7 @@ macht damit verhandelbar, was die Sicherheit kosten darf.
---
## Monte-Carlo-Simulation {#sec:unsicherheit-monte-carlo-simulation}
## Monte-Carlo-Simulation{idx:Monte-Carlo-Simulation} {#sec:unsicherheit-monte-carlo-simulation}
**Monte-Carlo**{idx:Monte-Carlo-Simulation} heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan
aus, und betrachte die **Verteilung** der Ergebnisse statt eines einzigen Werts.
@ -662,7 +662,7 @@ Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe *EVPI inte
---
## Robuste Optimierung: gegen den Worst Case absichern {#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
## Robuste Optimierung{idx:Robuste Optimierung}: gegen den Worst Case absichern {#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case}
Wenn Wahrscheinlichkeiten unbekannt oder instabil sind — Marktcrashs, Lieferkettenabrisse,
Pandemien —, hilft die **robuste Optimierung**{idx:Robuste Optimierung}. Sie fragt nicht nach dem Mittel, sondern
@ -856,19 +856,19 @@ z_{1-\alpha} = \Phi^{-1}(1-\alpha)
$$
Das ist eine **Second-Order-Cone-Bedingung**{idx:Konische Optimierung (SOCP, SDP)} — dieselbe
Bauform, die {ref:kap:qp-nlp} eingeführt hat, und CVXPY löst sie ohne Umstände.
Bauform, die {ref:kap:qp-nlp} eingeführt hat, und CVXPY{idx:CVXPY} löst sie ohne Umstände.
> **🎯 Warum eine Norm und keine Summe**
> Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von
> Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige
> Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das
> automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und
> würde Diversifikation nicht belohnen.
> würde Diversifikation{idx:Diversifikation} nicht belohnen.
### Weg 2: Szenarien — Big-M ohne Verteilungsannahme
Liegen statt einer Verteilung nur $S$ beobachtete Szenarien vor, bekommt jedes eine
Binärvariable $z_s$, die sagt, ob es verletzt werden **darf**:
Binärvariable{idx:Binärvariable} $z_s$, die sagt, ob es verletzt werden **darf**:
$$
\begin{aligned}
@ -1203,11 +1203,11 @@ Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verf
rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil
die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles
zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine
Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.
Kovarianzmatrix{idx:Kovarianzmatrix} mit Korrelationen um null kann so etwas nicht ausdrücken.
Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf
94,8 %, kostet dafür 16,7 % mehr — und kauft **keine Windkraft mehr**. Die Logik dahinter ist
hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
hart{idx:Nebenbedingung!hart} und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.
> **⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich**
> Bei $S$ Szenarien und $\alpha = 5\,\%$ darf das Modell genau $0{,}05 \cdot S$ davon
@ -1336,7 +1336,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
> {ref:sec:unsicherheit-schnellstart} führt der Mittelwert in die Irre, weil die **Kosten**
> asymmetrisch sind. Hier führt er in die Irre, weil die **Verknüpfung** asymmetrisch ist:
> Ein Gewerk, das früher fertig wird, hilft niemandem — ein Gewerk, das sich verspätet,
> verzögert alles. Beide Male gilt: *Der Mittelwert einer Funktion ist nicht die Funktion
> verzögert alles. Beide Male gilt: *Der Mittelwert einer Funktion{idx:Konvexität!Funktion} ist nicht die Funktion
> des Mittelwerts.*
---
@ -1368,7 +1368,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
> und rechnet deterministisch. Wann ist das unproblematisch?**
> (a) Immer — der Erwartungswert ist die beste Einzelschätzung.
> (b) Nie.
> (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen **linear** sind und
> (c) Wenn Zielfunktion{idx:Zielfunktion} und Nebenbedingungen in den unsicheren Größen **linear** sind und
> keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine
> Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.
@ -1421,7 +1421,7 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.)
Entscheidung nicht begründbar.
**Ausblick.** {ref:kap:dynamische-programmierung} fügt die Zeitdimension hinzu: Entscheidungen, die über viele
Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.
Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung{idx:Bellman-Gleichung}.
---

View file

@ -1,4 +1,4 @@
# Kapitel: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution {#kap:dynamische-programmierung}
# Kapitel: Dynamische Programmierung — Die Bellman-Gleichung{idx:Bellman-Gleichung} und Order-Execution {#kap:dynamische-programmierung}
> **📌 Kapitel auf einen Blick**
>
@ -8,7 +8,7 @@
> **Voraussetzungen:** {ref:kap:unsicherheit} (Erwartungswerte). Rekursion in Python.
>
> **Danach können Sie:** Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die
> Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und
> Wertfunktion durch Rückwärtsinduktion{idx:Rückwärtsinduktion} berechnen, die optimale Strategie ableiten — und
> prüfen, ob Ihr Zustand vollständig genug ist.
>
> **Zeitbedarf:** ca. 5 Stunden.
@ -85,7 +85,7 @@ zusätzlichen bezahlt.
**Warum funktioniert das?** Weil man den Rest des Weges nicht neu durchdenken muss, sobald
man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese
Eigenschaft heißt **Bellmansches Optimalitätsprinzip** und ist der Grund, warum aus einem
Eigenschaft heißt **Bellmansches Optimalitätsprinzip**{idx:Optimalitätsprinzip} und ist der Grund, warum aus einem
Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten
fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.
@ -99,10 +99,10 @@ Nach diesem Kapitel können Sie …
2. … Zustand, Stufe, Aktion und Wertfunktion für ein gegebenes Problem benennen.
3. … eine Rückwärtsinduktion{idx:Rückwärtsinduktion} von Hand für ein kleines Problem durchführen.
4. … das Almgren-Chriss-Ausführungsproblem lösen und die Lösung interpretieren.
5. … einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität“ zuschlägt.
5. … einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität{idx:Fluch der Dimensionalität}“ zuschlägt.
6. … die Wertfunktion als **Nachschlagetabelle** lesen: Sie liefert keine Planfolge, sondern
eine Regel, die auch bei Abweichungen gilt.
7. … einen Zustandsraum auf Vollständigkeit prüfen und erkennen, wann eine Kostenart
7. … einen Zustandsraum{idx:Zustandsraum} auf Vollständigkeit prüfen und erkennen, wann eine Kostenart
Vorgeschichte erfordert.
---
@ -328,13 +328,13 @@ C(n_t, X_t) = \underbrace{\eta\,n_t^2}_{\text{Marktauswirkung}} \;+\; \underbrac
$$
> **📐 Formel-Lesehilfe**
> * $n_t$ — in Periode $t$ verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als
> * $n_t$ — in Periode $t$ verkaufte Stückzahl. Quadriert, weil doppelte Menge{idx:Konvexität!Menge} mehr als
> doppelten Preisdruck erzeugt.
> * $\eta$ („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen?
> * $X_t$ — Restbestand **nach** dem Verkauf. Quadriert, weil Varianz quadratisch mit der
> Positionsgröße wächst.
> * $\lambda$ — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage?
> * $\sigma$ — Volatilität je Periode.
> * $\sigma$ — Volatilität{idx:Volatilität} je Periode.
>
> **Ohne Formel gesagt:** „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet
> Nervenkitzel. Finde die Mitte.“
@ -352,7 +352,7 @@ $$
> holding_risk = risk_aversion * (sigma_period ** 2) * (remaining ** 2) * 1e5
> ```
> Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte
> Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie
> Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter{idx:Parameter (Modell)} wie
> `risk_aversion = 1e-6` würde dann in Wahrheit $0{,}1$ bedeuten. Solche „magischen
> Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig
> ist.
@ -693,7 +693,7 @@ Zustandsvariable **multipliziert** den Aufwand.
**Gegenmittel:**
* **Zustandsraum**{idx:Zustandsraum} **verkleinern:** gröber diskretisieren, irrelevante Variablen weglassen.
* **Approximate Dynamic Programming**{idx:Dynamische Programmierung!Approximate Dynamic Programming}**:** $V_t$ durch eine parametrische Funktion annähern
* **Approximate Dynamic Programming**{idx:Dynamische Programmierung!Approximate Dynamic Programming}**:** $V_t$ durch eine parametrische Funktion{idx:Konvexität!Funktion} annähern
statt tabellarisch zu speichern.
* **Reinforcement Learning**{idx:Reinforcement Learning}**:** dieselbe Bellman-Gleichung{idx:Bellman-Gleichung}, aber $V$ wird aus Erfahrung
gelernt (Q-Learning{idx:Q-Learning}) statt vollständig berechnet.
@ -720,7 +720,7 @@ nicht nur vom aktuellen Zustand)?
Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit
`Bellman_Minimalbeispiel.py` (angepasst).
**Aufgabe ⭐⭐ — Rucksackproblem als DP.**
**Aufgabe ⭐⭐ — Rucksackproblem{idx:Rucksackproblem} als DP.**
Lösen Sie das Rucksackproblem aus {ref:kap:milp} mit dynamischer Programmierung statt MILP.
(Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie
Laufzeit und Ergebnis mit dem MILP-Solver.

View file

@ -10,7 +10,7 @@
> {ref:kap:milp}. Für den Denkfehler hilft {ref:kap:markowitz}.
>
> **Danach können Sie:** begründen, warum eine gewichtete Summe bei ganzzahligen Modellen
> Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige Pareto-Front mit dem
> Kompromisse **grundsätzlich** nicht erreichen kann; eine vollständige Pareto-Front{idx:Pareto-Front} mit dem
> ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen,
> die sie tatsächlich treffen kann.
>
@ -128,7 +128,7 @@ $$
> **📖 Definition: Pareto-Front**{idx:Pareto-Front}
>
> Die Menge aller nicht dominierten Lösungen. Sie ist das vollständige Ergebnis eines
> Die Menge{idx:Konvexität!Menge} aller nicht dominierten Lösungen. Sie ist das vollständige Ergebnis eines
> Mehrzielproblems — nicht ein Punkt, sondern eine Liste.
>
> Wer daraus einen einzelnen Punkt macht, trifft eine Entscheidung. Die Frage ist nur, ob er
@ -645,7 +645,7 @@ Eine Front mit zehn Punkten ist kein Ergebnis, sondern eine Speisekarte. Damit s
entscheidbar wird, gehört eine Spalte dazu, die im Programm `EUR je kg` heißt:
$$
\text{Schattenpreis} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
\text{Schattenpreis{idx:Schattenpreis}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
$$
@ -657,7 +657,7 @@ kaufmännisch schon getroffen.
>
> | Stelle | Was sie tut |
> | --- | --- |
> | `plane(ziel, co2_grenze, kosten_grenze)` | **eine** Funktion für alle Varianten. Welches Ziel und welche Schranke — mehr Unterschied gibt es zwischen den fünf Teilen des Programms nicht |
> | `plane(ziel, co2_grenze, kosten_grenze)` | **eine** Funktion{idx:Konvexität!Funktion} für alle Varianten. Welches Ziel und welche Schranke — mehr Unterschied gibt es zwischen den fünf Teilen des Programms nicht |
> | `pareto_front()` | das ε-Verfahren: Grenze jeweils um 1 kg unter den zuletzt erreichten Wert drücken, bis nichts mehr geht |
> | `untere_huelle()` | rechnet die Hülle **unabhängig** aus, damit die Aussage „unerreichbar“ nicht auf dem Gewichtsraster beruht, sondern auf Geometrie |
>
@ -683,7 +683,7 @@ würden Sie empfehlen — und wie begründen Sie ihn in einem Satz?
Erhöhen Sie `BAHN_TRASSEN` von 5 auf 8. Was passiert mit der Länge der Front und mit der
Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.
**Aufgabe ⭐⭐ — Die Front der Relaxation.**
**Aufgabe ⭐⭐ — Die Front der Relaxation{idx:Relaxation}.**
Lassen Sie `integrality` weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut
(mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt
oberhalb der konvexen Hülle — und warum?
@ -771,4 +771,4 @@ einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand
---
*Weiter mit:* [{ref:kap:prognose} — Predict-then-Optimize](34_Predict_then_Optimize.md)
*Weiter mit:* [{ref:kap:prognose} — Predict-then-Optimize{idx:Predict-then-Optimize}](34_Predict_then_Optimize.md)

View file

@ -1,4 +1,4 @@
# Kapitel: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung {#kap:prognose}
# Kapitel: Predict-then-Optimize{idx:Predict-then-Optimize} — die bessere Prognose, die schlechtere Entscheidung {#kap:prognose}
> **📌 Kapitel auf einen Blick**
>
@ -6,7 +6,7 @@
> Ein Modell **prognostiziert**, ein anderes **entscheidet**. Beide arbeiten sauber — und
> genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.
>
> **Voraussetzungen:** {ref:kap:unsicherheit}, insbesondere das Newsvendor-Problem und das
> **Voraussetzungen:** {ref:kap:unsicherheit}, insbesondere das Newsvendor-Problem{idx:Newsvendor-Problem} und das
> kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.
>
> **Danach können Sie:** begründen, warum ein Prognosemodell mit kleinerem MSE teurere
@ -61,7 +61,7 @@ derselben Daten, aus denen auch das Quantil kommt. Trotzdem kostet er **7,20 €
gut 10 %.
Der Grund steht in den Preisen: Ein fehlendes Brot kostet doppelt so viel wie ein übriges.
Bei asymmetrischen Kosten liegt die optimale Menge nicht in der Mitte der Verteilung,
Bei asymmetrischen Kosten liegt die optimale Menge{idx:Konvexität!Menge} nicht in der Mitte der Verteilung,
sondern beim **kritischen Verhältnis** $c_-/(c_- + c_+) = 6/9 = 66{,}7\,\%$ — das kennen Sie
aus {ref:kap:unsicherheit}.
@ -578,7 +578,7 @@ Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert" — i
denkbare Form einer Idee, die derzeit erforscht wird: **das Prognosemodell direkt auf die
Entscheidungskosten zu trainieren** statt auf ein Fehlermaß.
Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall
Hier war es ein einziger Parameter{idx:Parameter (Modell)}, mit einer Rasterschleife bestimmt. Der allgemeine Fall
ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten
der Kosten nach den Modellparametern zu bilden, muss man **durch das Optimierungsproblem
hindurch ableiten**.
@ -631,7 +631,7 @@ und zwar **in unterschiedlichem Ausmaß**? Was sagt das über den Wert eines Mer
>
> Ein Data-Science-Team stellt sein Quartalsergebnis vor:
>
> > *„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE
> > *„Wir haben das alte lineare Modell durch ein Gradient{idx:Gradient}-Boosting-Modell ersetzt. Der MSE
> > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte
> > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem
> > Monat die neuen Werte."*

View file

@ -24,7 +24,7 @@ Er hat drei Abschnitte, und Sie können nach jedem aufhören:
> ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas
> fehlt — die Methoden sind dieselben.
>
> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** Schätzfehler in
> **Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen:** Schätzfehler{idx:Schätzfehler} in
> Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei
> Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung
> mit sauberer Trennung von Trainings- und Testzeitraum.
@ -114,7 +114,7 @@ Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. **60,26 € je
Risikobudget** — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt,
was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen
Namen: der **Preis des Risikos**. Es ist nicht bloß eine Analogie, es ist derselbe
Schattenpreis aus {ref:kap:lp}.
Schattenpreis{idx:Schattenpreis} aus {ref:kap:lp}.
> **🎯 Merksatz**
> Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei
@ -122,7 +122,7 @@ Schattenpreis aus {ref:kap:lp}.
> — nicht daran, dass eine Tabelle es behauptet.
**Warum funktioniert das?** Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es
sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden,
sieht eine Matrix, einen Kapazitätsvektor{idx:Kapazitätsvektor} und einen Zielvektor. Ob in der Matrix Stunden,
Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.
---
@ -646,7 +646,7 @@ Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — u
Einschränkung dieser Aussage kennen Sie aus {ref:sec:lp-entartung}?
**Aufgabe ⭐⭐ — Die Brücke rückwärts.**
Übertragen Sie die Ledoit-Wolf-Shrinkage aus {ref:kap:finanzdaten} auf Lieferzeiten:
Übertragen Sie die Ledoit-Wolf-Shrinkage{idx:Ledoit-Wolf-Shrinkage} aus {ref:kap:finanzdaten} auf Lieferzeiten:
Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?
**Aufgabe ⭐⭐ — CVaR mit Ganzzahligkeit.**
@ -670,7 +670,7 @@ trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
> geht vor wie im Portfoliomanagement:
>
> > *„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und
> > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell
> > die Kovarianzmatrix{idx:Kovarianzmatrix} der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell
> > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer
> > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio."*
>
@ -723,7 +723,7 @@ trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.
## Zusammenfassung {#sec:bruecke-zusammenfassung}
* Die Behauptung „dieselben Methoden, andere Domäne" ist prüfbar — und hier geprüft: Ein
Depot passt unverändert in die Klasse `Produktionsproblem`, und **eine** CVaR-Funktion
Depot passt unverändert in die Klasse `Produktionsproblem`, und **eine** CVaR-Funktion{idx:Konvexität!Funktion}
bearbeitet Kursverluste wie Lieferverzüge.
* Der **Schattenpreis** einer Ressource heißt in der Finanzwelt *Preis des Risikos*. Es ist
derselbe Dualwert derselben Nebenbedingung.

Some files were not shown because too many files have changed in this diff Show more