<aclass="brand"href="index.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-book"></use></svg><span>Optimierte Entscheidungsfindung mit Python</span></a>
<navclass="sidebar"id="sidebar"aria-label="Kapitelnavigation"><divclass="sidebar-inhalt"><detailsclass="sidebar-gruppe"><summary>Einstieg</summary><ul><lidata-kapitel="vorwort.html"><ahref="vorwort.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><lidata-kapitel="notation.html"><ahref="notation.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><lidata-kapitel="einfuehrung.html"><ahref="einfuehrung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><lidata-kapitel="fundament.html"><ahref="fundament.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><lidata-kapitel="oekosystem.html"><ahref="oekosystem.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><lidata-kapitel="modellierung.html"><ahref="modellierung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><lidata-kapitel="synthese-grundlagen.html"><ahref="synthese-grundlagen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><lidata-kapitel="lp.html"><ahref="lp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><lidata-kapitel="milp.html"><ahref="milp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><lidata-kapitel="cpsat.html"><ahref="cpsat.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><lidata-kapitel="graphen.html"><ahref="graphen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><lidata-kapitel="metaheuristiken.html"><ahref="metaheuristiken.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><lidata-kapitel="dekomposition.html"><ahref="dekomposition.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><lidata-kapitel="synthese-kernverfahren.html"><ahref="synthese-kernverfahren.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><detailsclass="sidebar-gruppe"open><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiod
<navclass="breadcrumb"aria-label="Breadcrumb"><ahref="index.html">Start</a>›<span>Teil III</span>›<span>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span></nav>
<navclass="prev-next"><aclass="prev-next-knopf prev-next-prev"href="unsicherheit.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a><aclass="prev-next-knopf prev-next-next"href="mehrziel.html"><span><small>Weiter</small>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-right"></use></svg></a></nav>
<article>
<h1id="kap-dynamische-programmierung">Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</h1>
<divclass="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.</p>
<p><strong>Voraussetzungen:</strong><ahref="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> (Erwartungswerte). Rekursion in Python.</p>
<p><strong>Danach können Sie:</strong> Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.</p>
<p><strong>Zeitbedarf:</strong> ca. 5 Stunden.</p>
<p><strong>Notebook:</strong><ahref="Notebooks_04/dynamische-programmierung.ipynb">dynamische-programmierung.ipynb</a> — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten</p>
<spanid="cb1-14"><ahref="#cb1-14"aria-hidden="true"tabindex="-1"></a>weg, ort <spanclass="op">=</span> [<spanclass="st">"Start"</span>], <spanclass="st">"Start"</span></span>
<spanid="cb1-15"><ahref="#cb1-15"aria-hidden="true"tabindex="-1"></a><spanclass="cf">while</span> ort <spanclass="op">!=</span><spanclass="st">"Ziel"</span>:</span>
<spanid="cb1-16"><ahref="#cb1-16"aria-hidden="true"tabindex="-1"></a> ort <spanclass="op">=</span> plan[ort]</span>
<spanid="cb1-19"><ahref="#cb1-19"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Restkosten je Ort:"</span>, wert)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>Start -> Sued -> Ost -> Ziel = 14 min
<p><strong>Der optimale Weg beginnt mit dem <em>teureren</em> ersten Abschnitt.</strong> Start → Süd kostet 6 Minuten, Start → Nord nur 4. Wer vorne spart, landet bei 21 Minuten statt 14 — <strong>50 % mehr</strong>:</p>
<table>
<colgroup>
<colstyle="width: 30%"/>
<colstyle="width: 30%"/>
<colstyle="width: 40%"/>
</colgroup>
<thead>
<trclass="header">
<th>Vorgehen</th>
<th>Route</th>
<thstyle="text-align: right;">Dauer</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>Gierig: „nimm immer die kürzeste nächste Strecke“</td>
<p>Der Grund steht in der zweiten Ausgabezeile. <code>Restkosten je Ort</code> ist die <strong>Wertfunktion</strong>: Sie sagt für jeden Ort, was der ganze Rest von dort aus noch kostet, wenn man ab dort optimal weiterfährt. Und dort steht das Entscheidende: Von Nord aus sind es noch <strong>17</strong> Minuten, von Süd nur <strong>8</strong>. Die vier gesparten Minuten am Anfang werden später mit neun zusätzlichen bezahlt.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Eine Entscheidung ist nie für sich zu bewerten, sondern nur <strong>zusammen mit allem, was danach kommt</strong>. Genau das leistet die Wertfunktion: Sie fasst die gesamte Zukunft eines Zustands in einer einzigen Zahl zusammen. Und weil man die Zukunft kennen muss, bevor man die Gegenwart bewerten kann, rechnet dynamische Programmierung <strong>rückwärts</strong>.</p>
</blockquote>
<p><strong>Warum funktioniert das?</strong> 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 <strong>Bellmansches Optimalitätsprinzip</strong> 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.</p>
<li>… das Bellmansche Optimalitätsprinzip formulieren und erklären, warum es gilt.</li>
<li>… Zustand, Stufe, Aktion und Wertfunktion für ein gegebenes Problem benennen.</li>
<li>… eine Rückwärtsinduktion von Hand für ein kleines Problem durchführen.</li>
<li>… das Almgren-Chriss-Ausführungsproblem lösen und die Lösung interpretieren.</li>
<li>… einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität“ zuschlägt.</li>
<li>… die Wertfunktion als <strong>Nachschlagetabelle</strong> lesen: Sie liefert keine Planfolge, sondern eine Regel, die auch bei Abweichungen gilt.</li>
<li>… einen Zustandsraum auf Vollständigkeit prüfen und erkennen, wann eine Kostenart Vorgeschichte erfordert.</li>
</ol>
<hr/>
<h2id="sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip">13.3 Das Bellmansche Optimalitätsprinzip</h2>
<p>Richard Bellman formulierte 1957 das Grundprinzip der <strong>Dynamischen Programmierung (DP)</strong>:</p>
<blockquote>
<p><strong>Optimalitätsprinzip.</strong> Eine optimale Strategie hat die Eigenschaft, dass — unabhängig vom Anfangszustand und der Anfangsentscheidung — die verbleibenden Entscheidungen eine optimale Strategie bezüglich des Zustands bilden, der aus der ersten Entscheidung resultiert.</p>
</blockquote>
<p><strong>Ohne Fachsprache:</strong><em>Jedes Teilstück einer optimalen Route ist selbst eine optimale Route.</em> Wenn der beste Weg von Hamburg nach München über Kassel führt, dann ist der Teil von Kassel nach München auch der beste Weg von Kassel nach München. Sonst könnte man ihn ersetzen und wäre insgesamt besser.</p>
<td>Was kosten die optimalen Restentscheidungen?</td>
<td>Minimale Restkosten</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>🎯 Die Kunst liegt in der Zustandsdefinition</strong> Der Zustand muss <strong>alles</strong> enthalten, was für die Zukunft relevant ist — und <strong>nichts mehr</strong>. Zu wenig: Das Modell ist falsch (Markov-Eigenschaft verletzt). Zu viel: Der Zustandsraum explodiert. Diese Abwägung ist die eigentliche Modellierungsleistung bei DP.</p>
<p>mit der Endbedingung <spanclass="math inline">V_T(S_T) = g(S_T)</span>.</p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <spanclass="math inline">V_t(S_t)</span> — „Was kostet mich der Rest, wenn ich zum Zeitpunkt <spanclass="math inline">t</span> im Zustand <spanclass="math inline">S_t</span> bin und ab jetzt optimal handle?“ * <spanclass="math inline">C(S_t,a_t)</span> — die <strong>sofortigen</strong> Kosten der Aktion <spanclass="math inline">a_t</span>. * <spanclass="math inline">V_{t+1}(S_{t+1})</span> — die Restkosten <strong>danach</strong>, wieder optimal. * <spanclass="math inline">\gamma</span> — Diskontfaktor (bei kurzen Horizonten meist <spanclass="math inline">\gamma = 1</span>). * <spanclass="math inline">\mathbb{E}[\cdot]</span> — Erwartungswert, falls der Übergang zufällig ist.</p>
<p><strong>Ohne Formel gesagt:</strong> „Der Wert von hier ist: die Kosten des nächsten Schritts plus der Wert von dort — und zwar für denjenigen nächsten Schritt, bei dem diese Summe am kleinsten ist.“</p>
<p><strong>Warum rückwärts?</strong> Weil <spanclass="math inline">V_{t+1}</span> bekannt sein muss, bevor man <spanclass="math inline">V_t</span> berechnen kann. Am Ende (<spanclass="math inline">t = T</span>) ist der Wert bekannt — dort fängt man an und arbeitet sich nach vorn.</p>
</blockquote>
<divclass="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer: die Bellman-Gleichung Baustein für Baustein</strong></p>
<table>
<colgroup>
<colstyle="width: 50%"/>
<colstyle="width: 50%"/>
</colgroup>
<thead>
<trclass="header">
<th>Mathematik</th>
<th>Alltagssprache</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><spanclass="math inline">S_t</span></td>
<td>„Alles, was ich jetzt über meine Lage wissen muss.“ Restmenge, Lagerbestand, Position — <strong>und sonst nichts</strong>.</td>
</tr>
<trclass="even">
<td><spanclass="math inline">a_t</span></td>
<td>„Was tue ich jetzt?“ Die Entscheidung dieser Stufe.</td>
<td>„Am Ende steht fest, was die Endlage wert ist.“ Hier fängt die Rechnung an.</td>
</tr>
</tbody>
</table>
<p><strong>Die ganze Gleichung in einem Satz:</strong><em>Der Wert von hier ist: was der nächste Schritt kostet, plus was es von dort aus noch kostet — für denjenigen nächsten Schritt, bei dem diese Summe am kleinsten ist.</em></p>
<p><strong>Und die Zeile, die man sich merken sollte:</strong> Sobald Sie <spanclass="math inline">V_t</span> für alle Zustände kennen, ist die optimale Strategie eine reine <strong>Nachschlagetabelle</strong> — für jeden Zustand steht darin, was zu tun ist. Das ist der praktische Ertrag der ganzen Rechnung: nicht ein Plan, sondern eine <strong>Regel</strong>, die auch dann noch gilt, wenn es anders kommt als gedacht.</p>
</blockquote>
</div>
<blockquote>
<p><strong>✏️ Handrechnung 13.1: Rückwärtsinduktion von Hand</strong></p>
<p><strong>Problem:</strong> Sie müssen 3 Einheiten in 2 Perioden verkaufen. Sofortkosten je Periode: <spanclass="math inline">C(n) = n^2</span> (großer Verkauf drückt den Preis überproportional). In der letzten Periode muss alles weg.</p>
<p><strong>Schritt 1 — Endstufe <spanclass="math inline">t = 2</span>:</strong> Es sind keine Perioden mehr übrig, also <spanclass="math inline">V_2(0) = 0</span>, alle anderen Zustände sind unzulässig.</p>
<p><strong>Schritt 2 — Stufe <spanclass="math inline">t = 1</span></strong> (letzte Verkaufsperiode, alles muss weg):</p>
<p><strong>Ergebnis:</strong><spanclass="math inline">V_0(3) = 5</span>, erreicht durch <spanclass="math inline">n_0 = 1</span> (dann <spanclass="math inline">n_1 = 2</span>) <strong>oder</strong><spanclass="math inline">n_0 = 2</span> (dann <spanclass="math inline">n_1 = 1</span>). Zwei gleichwertige Optima.</p>
<p><strong>Die Lehre:</strong> Alles auf einmal zu verkaufen kostet 9, gleichmäßiges Aufteilen nur 5. Das ist der Kern des Ausführungsproblems: <strong>Bei überproportionalen Kosten lohnt sich Stückelung.</strong> Und beachten Sie, dass wir nur <spanclass="math inline">4 + 4 = 8</span> Kombinationen bewertet haben statt aller Verkaufspfade — bei größeren Problemen ist dieser Unterschied dramatisch.</p>
<spanid="cb3-5"><ahref="#cb3-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel Dynamische Programmierung: Die Handrechnung zur Rueckwaertsinduktion als Code.</span></span>
<spanid="cb3-6"><ahref="#cb3-6"aria-hidden="true"tabindex="-1"></a><spanclass="co">Zeigt die Wertfunktionstabelle und die optimale Politik Schritt fuer Schritt.</span></span>
<spanid="cb3-11"><ahref="#cb3-11"aria-hidden="true"tabindex="-1"></a>GESAMT <spanclass="op">=</span><spanclass="dv">3</span><spanclass="co"># zu verkaufende Einheiten</span></span>
<spanid="cb3-21"><ahref="#cb3-21"aria-hidden="true"tabindex="-1"></a><spanclass="co"># V[t, x] = minimale Restkosten, wenn zu Beginn von Periode t noch x Stueck offen sind</span></span>
<spanid="cb3-22"><ahref="#cb3-22"aria-hidden="true"tabindex="-1"></a> V <spanclass="op">=</span> np.full((PERIODEN <spanclass="op">+</span><spanclass="dv">1</span>, GESAMT <spanclass="op">+</span><spanclass="dv">1</span>), np.inf)</span>
<spanid="cb3-23"><ahref="#cb3-23"aria-hidden="true"tabindex="-1"></a> politik <spanclass="op">=</span> np.zeros((PERIODEN, GESAMT <spanclass="op">+</span><spanclass="dv">1</span>), dtype<spanclass="op">=</span><spanclass="bu">int</span>)</span>
<spanid="cb3-25"><ahref="#cb3-25"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Endbedingung: nach der letzten Periode darf nichts mehr offen sein</span></span>
<spanid="cb3-32"><ahref="#cb3-32"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(PERIODEN <spanclass="op">-</span><spanclass="dv">1</span>, <spanclass="op">-</span><spanclass="dv">1</span>, <spanclass="op">-</span><spanclass="dv">1</span>):</span>
<spanid="cb3-33"><ahref="#cb3-33"aria-hidden="true"tabindex="-1"></a> letzte_periode <spanclass="op">=</span> (t <spanclass="op">==</span> PERIODEN <spanclass="op">-</span><spanclass="dv">1</span>)</span>
<spanid="cb3-34"><ahref="#cb3-34"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Stufe t = </span><spanclass="sc">{</span>t<spanclass="sc">}</span><spanclass="ss">"</span><spanclass="op">+</span> (<spanclass="st">" (letzte Periode: alles muss weg)"</span><spanclass="cf">if</span> letzte_periode</span>
<spanid="cb3-35"><ahref="#cb3-35"aria-hidden="true"tabindex="-1"></a><spanclass="cf">else</span><spanclass="st">" (freie Wahl der Menge)"</span>))</span>
<spanid="cb3-40"><ahref="#cb3-40"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> x <spanclass="kw">in</span><spanclass="bu">range</span>(GESAMT <spanclass="op">+</span><spanclass="dv">1</span>):</span>
<spanid="cb3-64"><ahref="#cb3-64"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Vorwaertspfad: der optimalen Politik folgen ---------------------</span></span>
<spanid="cb3-70"><ahref="#cb3-70"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(PERIODEN):</span>
<spanid="cb3-73"><ahref="#cb3-73"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Periode </span><spanclass="sc">{</span>t<spanclass="sc">}</span><spanclass="ss">: Bestand </span><spanclass="sc">{</span>bestand<spanclass="sc">}</span><spanclass="ss"> -> verkaufe </span><spanclass="sc">{</span>aktion<spanclass="sc">}</span><spanclass="ss">"</span></span>
<spanid="cb3-82"><ahref="#cb3-82"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Zum Vergleich - alles in Periode 0 verkaufen: </span><spanclass="sc">{</span>alles_sofort<spanclass="sc">:.1f}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-83"><ahref="#cb3-83"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Ersparnis durch Stueckelung: </span><spanclass="sc">{</span>alles_sofort <spanclass="op">-</span> gesamtkosten<spanclass="sc">:.1f}</span><spanclass="ss">"</span></span>
<h2id="sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung">13.4 Das Almgren-Chriss-Problem: optimale Orderausführung</h2>
<p>Ein klassisches OR-Problem an Börsen: Ein institutioneller Händler muss <spanclass="math inline">X_0 = 100\,000</span> Aktien innerhalb von <spanclass="math inline">T</span> Handelsperioden verkaufen.</p>
<p><strong>Der Zielkonflikt:</strong></p>
<oltype="1">
<li><strong>Marktauswirkung (<em>market impact</em>, Slippage)</strong>. Verkauft man zu schnell, bricht das Orderbuch ein — der Ausführungspreis verschlechtert sich. Diese Kosten wachsen <strong>überproportional</strong> mit der Ordergröße.</li>
<li><strong>Zeitrisiko (<em>timing risk</em>).</strong> Wartet man zu lange, schwankt der Marktpreis. Je größer der noch offene Restbestand, desto mehr Geld liegt im Risiko.</li>
</ol>
<p>Das Modell nach Almgren und Chriss (2000) minimiert die Summe aus beidem:</p>
<p><spanclass="math display">
C(n_t, X_t) = \underbrace{\eta\,n_t^2}_{\text{Marktauswirkung}} \;+\; \underbrace{\lambda\,\sigma^2 X_t^2}_{\text{Risiko des Restbestands}}
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <spanclass="math inline">n_t</span> — in Periode <spanclass="math inline">t</span> verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * <spanclass="math inline">\eta</span> („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * <spanclass="math inline">X_t</span> — Restbestand <strong>nach</strong> dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * <spanclass="math inline">\lambda</span> — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * <spanclass="math inline">\sigma</span> — Volatilität je Periode.</p>
<p><strong>Ohne Formel gesagt:</strong> „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet Nervenkitzel. Finde die Mitte.“</p>
<p><strong>Die zwei Extremfälle:</strong> * <spanclass="math inline">\lambda \to 0</span> (risikoneutral): gleichmäßiges Aufteilen auf alle Perioden minimiert <spanclass="math inline">\sum n_t^2</span> bei fester Summe. * <spanclass="math inline">\lambda \to \infty</span> (extrem risikoscheu): sofort alles verkaufen, um kein Risiko zu tragen.</p>
</blockquote>
<blockquote>
<p><strong>⚠️ Zur Kalibrierung des Risikoparameters</strong></p>
<p>Ein häufiger Fehler ist ein undokumentierter Skalierungsfaktor im Risikoterm, etwa:</p>
<p>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 <code>risk_aversion = 1e-6</code> würde dann in Wahrheit <spanclass="math inline">0{,}1</span> bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.</p>
<p><strong>Der Parameter lässt sich stattdessen herleiten.</strong> Beide Kostenterme werden in <strong>Euro</strong> gerechnet: * Marktauswirkung: <spanclass="math inline">\eta \cdot n_t^2</span> mit <spanclass="math inline">\eta</span> in €/Stück². * Risiko: <spanclass="math inline">\tfrac{\lambda}{2}\cdot \sigma_{\text{Periode}}^2 \cdot P_0^2 \cdot X_t^2</span> — die Varianz des Werts der offenen Position, multipliziert mit der Risikoaversion.</p>
<p>Durch die Multiplikation mit <spanclass="math inline">P_0^2</span> (dem Quadrat des Aktienkurses) stimmen die Einheiten, und <spanclass="math inline">\lambda</span> wird interpretierbar: Es ist der Preis, den man je Einheit Wertvarianz zu zahlen bereit ist.</p>
<spanid="cb5-6"><ahref="#cb5-6"aria-hidden="true"tabindex="-1"></a><spanclass="co">(Almgren-Chriss-Rahmen, geloest per Rueckwaertsinduktion).</span></span>
<spanid="cb5-10"><ahref="#cb5-10"aria-hidden="true"tabindex="-1"></a><spanclass="co"> ohne undokumentierte Skalierungsfaktoren</span></span>
<spanid="cb5-11"><ahref="#cb5-11"aria-hidden="true"tabindex="-1"></a><spanclass="co"> * Vergleich mit der analytischen Almgren-Chriss-Loesung</span></span>
<spanid="cb5-12"><ahref="#cb5-12"aria-hidden="true"tabindex="-1"></a><spanclass="co"> * Vergleich mit naiven Strategien (alles sofort / gleichmaessig)</span></span>
<spanid="cb5-13"><ahref="#cb5-13"aria-hidden="true"tabindex="-1"></a><spanclass="co"> * Sensitivitaet gegenueber der Risikoaversion</span></span>
<spanid="cb5-28"><ahref="#cb5-28"aria-hidden="true"tabindex="-1"></a>GESAMTBESTAND <spanclass="op">=</span><spanclass="dv">100_000</span><spanclass="co"># X_0, zu verkaufende Aktien</span></span>
<spanid="cb5-29"><ahref="#cb5-29"aria-hidden="true"tabindex="-1"></a>PERIODEN <spanclass="op">=</span><spanclass="dv">5</span><spanclass="co"># T Handelsperioden</span></span>
<spanid="cb5-30"><ahref="#cb5-30"aria-hidden="true"tabindex="-1"></a>KURS <spanclass="op">=</span><spanclass="fl">50.0</span><spanclass="co"># P_0 in EUR, zur Skalierung des Risikoterms</span></span>
<spanid="cb5-31"><ahref="#cb5-31"aria-hidden="true"tabindex="-1"></a>ETA <spanclass="op">=</span><spanclass="fl">2.5e-6</span><spanclass="co"># EUR je Stueck^2 (Slippage-Koeffizient)</span></span>
<spanid="cb5-35"><ahref="#cb5-35"aria-hidden="true"tabindex="-1"></a>SCHRITTWEITE <spanclass="op">=</span><spanclass="dv">1000</span><spanclass="co"># Diskretisierung des Zustandsraums</span></span>
<spanid="cb5-37"><ahref="#cb5-37"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Kalibrierungshinweis: Die dimensionslose Kennzahl des Modells ist</span></span>
<spanid="cb5-42"><ahref="#cb5-42"aria-hidden="true"tabindex="-1"></a><spanclass="co"># >> 1 -> fast alles sofort verkaufen</span></span>
<spanid="cb5-43"><ahref="#cb5-43"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Genau diese Interpretierbarkeit geht mit einem undokumentierten</span></span>
<spanid="cb5-54"><ahref="#cb5-54"aria-hidden="true"tabindex="-1"></a><spanclass="co"> -> P0^2 macht aus "Stueck^2" einen EUR^2-Wert; lambda hat damit</span></span>
<spanid="cb5-55"><ahref="#cb5-55"aria-hidden="true"tabindex="-1"></a><spanclass="co"> die Einheit 1/EUR und ist interpretierbar.</span></span>
<spanid="cb5-64"><ahref="#cb5-64"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Rueckwaertsinduktion ueber den diskretisierten Zustandsraum."""</span></span>
<spanid="cb5-68"><ahref="#cb5-68"aria-hidden="true"tabindex="-1"></a> V <spanclass="op">=</span> np.full((PERIODEN <spanclass="op">+</span><spanclass="dv">1</span>, anzahl), np.inf)</span>
<spanid="cb5-69"><ahref="#cb5-69"aria-hidden="true"tabindex="-1"></a> politik <spanclass="op">=</span> np.zeros((PERIODEN, anzahl), dtype<spanclass="op">=</span><spanclass="bu">int</span>)</span>
<spanid="cb5-70"><ahref="#cb5-70"aria-hidden="true"tabindex="-1"></a> V[PERIODEN, <spanclass="dv">0</span>] <spanclass="op">=</span><spanclass="fl">0.0</span><spanclass="co"># am Ende muss alles verkauft sein</span></span>
<spanid="cb5-72"><ahref="#cb5-72"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(PERIODEN <spanclass="op">-</span><spanclass="dv">1</span>, <spanclass="op">-</span><spanclass="dv">1</span>, <spanclass="op">-</span><spanclass="dv">1</span>):</span>
<spanid="cb5-73"><ahref="#cb5-73"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> idx, bestand <spanclass="kw">in</span><spanclass="bu">enumerate</span>(zustaende):</span>
<spanid="cb5-74"><ahref="#cb5-74"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> t <spanclass="op">==</span> PERIODEN <spanclass="op">-</span><spanclass="dv">1</span>:</span>
<spanid="cb5-75"><ahref="#cb5-75"aria-hidden="true"tabindex="-1"></a> moegliche <spanclass="op">=</span> [bestand] <spanclass="co"># letzte Periode: Rest muss weg</span></span>
<spanid="cb5-95"><ahref="#cb5-95"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Geschlossene Almgren-Chriss-Loesung fuer den kontinuierlichen Fall.</span></span>
<spanid="cb5-96"><ahref="#cb5-96"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Der optimale Pfad ist X_t = X_0 * sinh(kappa*(T-t)) / sinh(kappa*T)</span></span>
<spanid="cb5-97"><ahref="#cb5-97"aria-hidden="true"tabindex="-1"></a><spanclass="co"> mit kappa = arccosh(tilde_kappa^2/2 + 1), tilde_kappa^2 = lambda*sigma^2*P0^2/eta.</span></span>
<spanid="cb5-98"><ahref="#cb5-98"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Dient hier als unabhaengige Kontrolle des DP-Ergebnisses.</span></span>
<spanid="cb5-103"><ahref="#cb5-103"aria-hidden="true"tabindex="-1"></a><spanclass="cf">return</span> np.linspace(GESAMTBESTAND, <spanclass="dv">0</span>, PERIODEN <spanclass="op">+</span><spanclass="dv">1</span>)</span>
<spanid="cb5-104"><ahref="#cb5-104"aria-hidden="true"tabindex="-1"></a> t <spanclass="op">=</span> np.arange(PERIODEN <spanclass="op">+</span><spanclass="dv">1</span>)</span>
<spanid="cb5-109"><ahref="#cb5-109"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Gesamtkosten eines beliebigen Bestandspfades."""</span></span>
<spanid="cb5-111"><ahref="#cb5-111"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(<spanclass="bu">len</span>(bestaende) <spanclass="op">-</span><spanclass="dv">1</span>):</span>
<spanid="cb5-120"><ahref="#cb5-120"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Vorwaertspfad der optimalen Politik -----------------------------</span></span>
<spanid="cb5-121"><ahref="#cb5-121"aria-hidden="true"tabindex="-1"></a> bestand <spanclass="op">=</span> GESAMTBESTAND</span>
<spanid="cb5-124"><ahref="#cb5-124"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(PERIODEN):</span>
<spanid="cb5-138"><ahref="#cb5-138"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"= </span><spanclass="sc">{</span>VOLA_PERIODE<spanclass="op">*</span><spanclass="dv">100</span><spanclass="sc">:.3f}</span><spanclass="ss"> % je Periode"</span>)</span>
<spanid="cb5-151"><ahref="#cb5-151"aria-hidden="true"tabindex="-1"></a> } <spanclass="cf">for</span> t <spanclass="kw">in</span><spanclass="bu">range</span>(PERIODEN)])</span>
<spanid="cb5-154"><ahref="#cb5-154"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Vergleich mit Alternativen und der analytischen Loesung ---------</span></span>
<spanid="cb5-178"><ahref="#cb5-178"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Sensitivitaet gegenueber der Risikoaversion ---------------------</span></span>
<spanid="cb5-179"><ahref="#cb5-179"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Wie wirkt die Risikoaversion? ---"</span>)</span>
<spanid="cb5-180"><ahref="#cb5-180"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'lambda'</span><spanclass="sc">:>10}</span><spanclass="ss"> | </span><spanclass="sc">{</span><spanclass="st">'Verkauf in Periode 0'</span><spanclass="sc">:>22}</span><spanclass="ss"> | </span><spanclass="sc">{</span><spanclass="st">'Charakter'</span><spanclass="sc">:<28}</span><spanclass="ss">"</span>)</span>
<spanid="cb5-185"><ahref="#cb5-185"aria-hidden="true"tabindex="-1"></a> erste <spanclass="op">=</span> pol_l[<spanclass="dv">0</span>, <spanclass="op">-</span><spanclass="dv">1</span>]</span>
<spanid="cb5-186"><ahref="#cb5-186"aria-hidden="true"tabindex="-1"></a> anteil <spanclass="op">=</span> erste <spanclass="op">/</span> GESAMTBESTAND <spanclass="op">*</span><spanclass="dv">100</span></span>
<spanid="cb5-187"><ahref="#cb5-187"aria-hidden="true"tabindex="-1"></a> charakter <spanclass="op">=</span> (<spanclass="st">"nahezu gleichmaessig"</span><spanclass="cf">if</span> anteil <spanclass="op"><</span><spanclass="dv">25</span><spanclass="cf">else</span></span>
<spanid="cb5-188"><ahref="#cb5-188"aria-hidden="true"tabindex="-1"></a><spanclass="st">"front-loaded"</span><spanclass="cf">if</span> anteil <spanclass="op"><</span><spanclass="dv">60</span><spanclass="cf">else</span><spanclass="st">"fast alles sofort"</span>)</span>
<spanid="cb5-203"><ahref="#cb5-203"aria-hidden="true"tabindex="-1"></a> plt.ylabel(<spanclass="st">"Verbleibender Bestand $X_t$"</span>)</span>
<spanid="cb5-208"><ahref="#cb5-208"aria-hidden="true"tabindex="-1"></a> ziel <spanclass="op">=</span> os.path.join(OUTPUT_DIR, <spanclass="st">"optimal_execution_dp.png"</span>)</span>
<spanid="cb5-210"><ahref="#cb5-210"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss">Diagramm gespeichert unter '</span><spanclass="sc">{</span>ziel<spanclass="sc">}</span><spanclass="ss">'"</span>)</span>
<p><strong>Die Lösung ist <em>front-loaded</em>:</strong> 41 % im ersten Schritt, dann fallend (25 %, 16 %, 10 %, 8 %). Das ist die typische Form — man baut Risiko früh ab, aber nicht abrupt. Die Sensitivitätstabelle zeigt, wie <spanclass="math inline">\lambda</span> zwischen den beiden Extremen steuert: Bei <spanclass="math inline">\lambda = 10^{-7}</span> verkauft das Modell gleichmäßig (Risiko ist egal), bei <spanclass="math inline">\lambda = 10^{-3}</span> praktisch alles sofort (Risiko dominiert).</p>
<p>Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung <strong>2 976 €</strong> oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.</p>
<blockquote>
<p><strong>💻 Code-Durchgang: die analytische Gegenprobe und was ihre Abweichung bedeutet</strong></p>
<p>Das Almgren-Chriss-Problem hat eine <strong>geschlossene Lösung</strong>: <spanclass="math display">X_t = X_0\,\frac{\sinh(\kappa(T-t))}{\sinh(\kappa T)}, \qquad \kappa = \operatorname{arcosh}\!\Big(\tfrac{\tilde\kappa^2}{2}+1\Big),\qquad \tilde\kappa^2 = \frac{\lambda\,\sigma^2 P_0^2}{\eta}</span></p>
<p>Sie liefert hier 10 860 € gegenüber 10 266 € beim DP — <strong>eine Abweichung von 5,8 %, und zwar zugunsten des DP.</strong> Das ist kein Fehler, sondern lehrreich, und es hat zwei Ursachen:</p>
<oltype="1">
<li><strong>Unterschiedliche Zeitkonvention.</strong> Die geschlossene Formel gilt für die zeitkontinuierliche Variante des Modells, in der das Risiko über das gesamte Intervall integriert wird. Unser diskretes Modell belastet dagegen den Bestand <strong>nach</strong> dem Verkauf. Beide Varianten sind legitim, sie lösen aber leicht verschiedene Probleme.</li>
<li><strong>Diskretisierung.</strong> Der Zustandsraum ist in 1000er-Schritten gerastert; die analytische Lösung darf beliebige Stückzahlen wählen.</li>
</ol>
<p><strong>Die Lehre daraus ist wichtiger als die Zahl:</strong> Eine Vergleichsrechnung, die <em>ungefähr</em> passt, bestätigt die Größenordnung und die Form der Lösung — beide Pfade sind front-loaded, beide liegen bei rund 21 Basispunkten. Sie beweist aber nicht die Punktgenauigkeit, solange die Modellkonventionen nicht identisch sind. <strong>Wer zwei Zahlen vergleicht, muss zuerst prüfen, ob sie dasselbe messen.</strong></p>
<p><strong>Übernehmen Sie dennoch das Prinzip:</strong> Wo immer eine unabhängige zweite Rechnung möglich ist — Formel, Simulation, Handrechnung —, bauen Sie sie ein. Hätte das DP hier 500 000 € oder 12 € geliefert, wäre der Fehler sofort aufgefallen.</p>
</blockquote>
<hr/>
<h2id="sec:dynamische-programmierung-der-fluch-der-dimensionalitaet">13.5 Der Fluch der Dimensionalität</h2>
<p>DP ist mächtig, aber es hat eine harte Grenze. Der Aufwand der Rückwärtsinduktion ist</p>
<p>— Perioden mal Zustände mal Aktionen. Im Beispiel oben: <spanclass="math inline">5 \times 101 \times 101 \approx 51\,000</span> Auswertungen, in Sekundenbruchteilen erledigt.</p>
<p>Das Problem entsteht, sobald der Zustand <strong>mehrere Dimensionen</strong> hat:</p>
<p><strong>Das ist der Fluch der Dimensionalität</strong> (Bellmans eigener Begriff): Jede zusätzliche Zustandsvariable <strong>multipliziert</strong> den Aufwand.</p>
<p><strong>Gegenmittel:</strong> * <strong>Zustandsraum</strong><strong>verkleinern:</strong> gröber diskretisieren, irrelevante Variablen weglassen. * <strong>Approximate Dynamic Programming</strong><strong>:</strong><spanclass="math inline">V_t</span> durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * <strong>Reinforcement Learning</strong><strong>:</strong> dieselbe Bellman-Gleichung, aber <spanclass="math inline">V</span> wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * <strong>Nach geschlossenen Lösungen suchen</strong> — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.</p>
<p><strong>Aufgabe 13.1 ⭐ — Bausteine benennen.</strong> Ein Wanderer plant eine 5-Tages-Tour und muss täglich entscheiden, wie weit er läuft. Bestimmen Sie Stufe, Zustand, Aktion und Wertfunktion. Was gehört <strong>nicht</strong> in den Zustand?</p>
<p><strong>Aufgabe 13.2 ⭐ — Optimalitätsprinzip anwenden.</strong> Warum folgt aus dem Optimalitätsprinzip, dass man rückwärts rechnen darf? Was würde schiefgehen, wenn die Kosten einer Periode auch von <strong>früheren</strong> Aktionen abhängen (und nicht nur vom aktuellen Zustand)?</p>
<p><strong>Aufgabe 13.3 ⭐⭐ — Rückwärtsinduktion von Hand.</strong> 5 Einheiten in 3 Perioden, Kosten <spanclass="math inline">C(n) = n^2 + 2n</span>. Erstellen Sie die vollständige Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit <code>Bellman_Minimalbeispiel.py</code> (angepasst).</p>
<p><strong>Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP.</strong> Lösen Sie das Rucksackproblem aus <ahref="milp.html#kap-milp">Kapitel 6</a> mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.</p>
<p><strong>Aufgabe 13.5 ⭐⭐⭐ — Risikoaversion kalibrieren.</strong> Untersuchen Sie mit <code>Mehrperiodige_Order_Execution.py</code>: (a) Bei welchem <spanclass="math inline">\lambda</span> verkauft das Modell in der ersten Periode mehr als 50 %? (b) Stellen Sie den Zusammenhang zwischen <spanclass="math inline">\lambda</span> und den erwarteten Gesamtkosten dar. (c) Ein Händler sagt: „Ich will höchstens 20 % Marktauswirkungskosten und den Rest an Risiko.“ Welches <spanclass="math inline">\lambda</span> setzen Sie?</p>
<p><strong>Aufgabe 13.6 ⭐⭐⭐ — Zustandsraum erweitern.</strong> Erweitern Sie das Ausführungsmodell um einen zweiten Zustand: die aktuelle <strong>Orderbuchtiefe</strong> (3 Stufen: dünn/normal/tief), die mit gegebenen Übergangswahrscheinlichkeiten wechselt und <spanclass="math inline">\eta</span> um Faktor 2 / 1 / 0,5 skaliert. Wie ändert sich die Strategie? Wie stark wächst die Rechenzeit?</p>
<hr/>
<h2id="sec:dynamische-programmierung-denkfehler">13.7 Finde den Denkfehler</h2>
<p>Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder Misserfolg: <strong>Was gehört in den Zustand?</strong> Ist er zu klein, rechnet das Verfahren völlig korrekt — nur eben an einem anderen Problem.</p>
<p>Eine Werkstatt plant vier Perioden. Der Bedarf beträgt 3, 1, 4, 2 Stück; produziert werden können höchstens 5 je Periode, gelagert höchstens 6 Stück.</p>
<table>
<colgroup>
<colstyle="width: 42%"/>
<colstyle="width: 57%"/>
</colgroup>
<thead>
<trclass="header">
<th>Kostenart</th>
<thstyle="text-align: right;">Höhe</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>Stückkosten</td>
<tdstyle="text-align: right;">4 €</td>
</tr>
<trclass="even">
<td>Lagerkosten je Stück und Periode</td>
<tdstyle="text-align: right;">2 €</td>
</tr>
<trclass="odd">
<td><strong>Rüstkosten</strong> — fallen an, wenn produziert wird <strong>und in der Vorperiode nicht</strong></td>
<tdstyle="text-align: right;">30 €</td>
</tr>
</tbody>
</table>
<p>Der Entwickler überlegt: <em>„Für die Zukunft zählt nur, wie viel ich auf Lager habe. Also ist der Lagerbestand mein Zustand.“</em></p>
<spanid="cb7-5"><ahref="#cb7-5"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> t <spanclass="op">==</span><spanclass="dv">4</span>:</span>
<spanid="cb7-7"><ahref="#cb7-7"aria-hidden="true"tabindex="-1"></a> best <spanclass="op">=</span> (<spanclass="bu">float</span>(<spanclass="st">"inf"</span>), ())</span>
<spanid="cb7-8"><ahref="#cb7-8"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> p <spanclass="kw">in</span><spanclass="bu">range</span>(KAP <spanclass="op">+</span><spanclass="dv">1</span>):</span>
<spanid="cb7-9"><ahref="#cb7-9"aria-hidden="true"tabindex="-1"></a> neu <spanclass="op">=</span> lager <spanclass="op">+</span> p <spanclass="op">-</span> BEDARF[t]</span>
<spanid="cb7-10"><ahref="#cb7-10"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> neu <spanclass="op"><</span><spanclass="dv">0</span><spanclass="kw">or</span> neu <spanclass="op">></span> LAGER_MAX:</span>
<p><strong>Ergebnis:</strong> Plan <spanclass="math inline">(5, 0, 5, 0)</span> für <strong>110 €</strong>. Nachgerechnet stimmt der Betrag sogar — dieser Plan kostet tatsächlich 110 €. Das wahre Optimum lautet aber <spanclass="math inline">(3, 1, 4, 2)</span> und kostet <strong>70 €</strong>.</p>
<p><strong>Ihre Aufgabe:</strong> (a) Sehen Sie sich die Zeile mit den Rüstkosten an. Welche Information bräuchte sie, die im Zustand <code>(t, lager)</code> nicht enthalten ist? (b) Welchen Vorteil des Plans <spanclass="math inline">(3,1,4,2)</span> kann dieses Modell prinzipiell nicht erkennen? (c) Wie lautet der korrigierte Zustand, und um welchen Faktor wächst der Zustandsraum dadurch? (d) Warum ist es besonders tückisch, dass der ausgegebene Kostenbetrag <em>richtig</em> war?</p>
<p><strong>🎯 Merksatz</strong> Der Zustand muss <strong>alles</strong> enthalten, was die Zukunft beeinflusst — und <strong>nichts</strong> darüber hinaus. Zu wenig, und das Modell löst ein anderes Problem, ohne es zu merken. Zu viel, und es wird unnötig groß (<ahref="#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet">Abschnitt 13.5</a>). Die Probe dafür ist ein einziger Satz: <em>„Wenn ich nur den Zustand kenne und nicht den Weg dorthin — kann ich dann noch optimal weiterentscheiden?“</em> Lautet die Antwort nein, fehlt etwas.</p>
<p>Genau eine Antwort ist jeweils richtig. Auflösung in <ahref="anhang-loesungen.html#quiz-loesung-dynamische-programmierung">Anhang A</a>.</p>
<p><strong>1. Warum rechnet dynamische Programmierung rückwärts?</strong> (a) Weil Rekursion in Python rückwärts effizienter ist. (b) Weil <spanclass="math inline">V_t</span> den Wert <spanclass="math inline">V_{t+1}</span> voraussetzt: Man kann eine Entscheidung erst bewerten, wenn man weiß, was sie für die Zukunft bedeutet. Am Ende ist dieser Wert bekannt — dort beginnt man. (c) Weil die Kosten in späteren Perioden höher sind.</p>
<p><strong>2. Was besagt das Bellmansche Optimalitätsprinzip?</strong> (a) Jede optimale Lösung besteht aus lauter einzeln optimalen Schritten — man kann also gierig vorgehen. (b) Ist ein Weg insgesamt optimal, so ist auch sein <strong>Reststück</strong> ab jedem Zwischenzustand optimal. Deshalb genügt es, je Zustand einen einzigen Wert zu speichern. (c) Bei genügend Rechenzeit findet man das Optimum immer.</p>
<p><strong>3. Ihr DP-Modell für eine Lagerplanung hat den Zustand „Lagerbestand“. Nun kommt eine Mengenrabattstaffel dazu, die sich nach der </strong>bisher im Jahr bestellten Gesamtmenge** richtet. Was folgt?** (a) Nichts — der Rabatt betrifft nur die Kosten, nicht den Zustand. (b) Die kumulierte Jahresmenge muss in den Zustand, sonst kann das Modell den Rabatt nicht korrekt zuordnen. Der Zustandsraum wird dadurch erheblich größer. (c) Man muss auf ein MILP wechseln, DP ist hier grundsätzlich ungeeignet.</p>
<li><strong>Das Optimalitätsprinzip</strong> erlaubt es, ein mehrstufiges Problem in ineinandergreifende Einperiodenprobleme zu zerlegen.</li>
<li><strong>Rückwärtsinduktion</strong> startet am bekannten Ende und arbeitet sich nach vorn — dadurch ist <spanclass="math inline">V_{t+1}</span> immer schon bekannt, wenn <spanclass="math inline">V_t</span> berechnet wird.</li>
<li><strong>Die Zustandsdefinition ist die eigentliche Modellierungsleistung</strong>: vollständig, aber so knapp wie möglich. Die Probe dafür ist ein Satz: <em>„Wenn ich nur den Zustand kenne und nicht den Weg dorthin — kann ich dann noch richtig weiterentscheiden?“</em> Fehlt etwas, rechnet das Verfahren korrekt an einem anderen Problem, ohne es zu melden.</li>
<li><strong>Die Wertfunktion ist eine Nachschlagetabelle, kein Plan.</strong> Ihr praktischer Ertrag ist eine Regel für jeden Zustand — die auch dann noch gilt, wenn es anders kommt als gedacht.</li>
<li><strong>Gierige Vorwärtsregeln scheitern systematisch</strong>, weil sie die Restkosten nicht kennen: Im Schnellstart kostet der billigere erste Abschnitt am Ende 50 % mehr.</li>
<li><strong>Bei überproportionalen Kosten lohnt sich Stückelung</strong> — das ist der ökonomische Kern des Ausführungsproblems.</li>
<li><strong>Magische Konstanten sind ein Warnsignal.</strong> Jeder Parameter braucht eine Einheit und eine Interpretation, sonst ist das Modell nicht kalibrierbar.</li>
<li><strong>Der Fluch der Dimensionalität</strong> begrenzt DP auf wenige Zustandsdimensionen.</li>
</ul>
<p><strong>Ausblick.</strong><ahref="bruecke.html#teil-anwendungen">Teil IV</a> führt alles zusammen: Ab <ahref="finanzdaten.html#kap-finanzdaten">Kapitel 18</a> arbeiten wir mit echten Marktdaten — und lernen zuerst, warum diese Daten trügerisch sind.</p>