operations_research/OR_HTML_04/unsicherheit.html
dschlueter 35cc0da166 Achter Fund (Uebung-Verweise) und Phase 8.3: Dockerfile
Zwei Dinge in einem Commit, weil beide den Plan abschliessen.

ACHTER FUND: sieben Saetze der Bauart "Uebung 8.5 laesst Sie diese Balance
untersuchen" - und sechs davon standen in alter Zaehlung. "8" war in Version
03 das QP/NLP-Kapitel, heute ist es Kapitel 11. Dieselbe Familie wie die
Denkfehler-Verweise aus 6.1a, nur mit einem Wort, das keine der bestehenden
Pruefungen kannte.

Aufgaben haben kein eigenes Label, ein {ref:} auf eine einzelne Aufgabe ist
also nicht moeglich. Verwiesen wird stattdessen auf Abschnitt plus
Aufgabentitel - und der ist stabil. Jedes Ziel wurde einzeln ueber die alte
Zaehlung bestimmt und am Zusammenhang geprueft.

Ein Fall war knifflig: "Uebung 6.7 (Wochendienstplan)" meinte die siebte
CP-SAT-Aufgabe der alten Zaehlung, also "Eigener Dienstplan" - die heute an
achter Stelle steht, weil in Phase 6.3 eine Aufgabe davor eingefuegt wurde.
Wer nur die Kapitelnummer angepasst haette, waere bei der falschen Aufgabe
gelandet.

--check kennt jetzt auch "Uebung"/"Übung". Gegengetestet.

PHASE 8.3: Dockerfile, zweistufig. Die erste Stufe uebersetzt die
Abhaengigkeiten in eine virtuelle Umgebung und braucht dafuer einen
Compiler, die zweite kopiert nur /opt/venv. Installiert werden die Gruppen
finance, large-scale, api und dev aus pyproject.toml; figures fehlt bewusst,
weil es zusaetzlich Graphviz verlangt.

Es wurde nicht behauptet, sondern gebaut. Ergebnis: 1,31 GB, und darin der
Installationstest mit allen drei Solver-Funktionstests bestanden, die 33
pytest-Tests bestanden und alle 69 netzfreien Programme fehlerfrei -
einschliesslich der drei aus 8.2, deren spawn-Isolation im Container ebenso
traegt wie ausserhalb.

Zwei Dinge, die der Bau gelehrt hat: libgomp1 fehlt im python:3.12-slim-Image
und wird von OR-Tools und HiGHS zur Laufzeit gebraucht (sonst
"libgomp.so.1: cannot open shared object file"). Und ein eigener Fehler:
USER kurs stand vor dem mkdir /buch/output, /buch gehoert root, der Bau
brach in der letzten Zeile ab. Beides steht jetzt als Kommentar im
Dockerfile.

Das Image fuehrt die Programme aus und baut das Buch nicht. Ein
.dockerignore haelt Website, PDF und Notebooks aus dem Build-Kontext. Und es
enthaelt ortools UND highspy, obwohl sie sich nicht gemeinsam importieren
lassen - der Konflikt wird zur Laufzeit durch getrennte Prozesse geloest,
nicht durch Weglassen.

Damit ist Phase 8 abgeschlossen und der Plan abgearbeitet.

Stand: 825 Querverweise, 76 Programme, 33 pytest-Tests, PDF 760 Seiten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 13:18:05 +02:00

1277 lines
186 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit · Optimierte Entscheidungsfindung mit Python</title>
<script>
(function () {
try {
var t = localStorage.getItem("or-theme");
if (t) document.documentElement.setAttribute("data-theme", t);
} catch (e) {}
})();
</script>
<link rel="stylesheet" href="assets/highlight.css" />
<link rel="stylesheet" href="katex/katex.min.css" />
<script defer="" src="katex/katex.min.js"></script>
<script>document.addEventListener("DOMContentLoaded", function () {
var mathElements = document.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains('display'),
throwOnError: false,
macros: macros,
fleqn: false
});
}}
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
if (location.hash) {
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
}
});
</script>
<link rel="stylesheet" href="assets/site.css" />
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
</symbol>
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="4.5"/>
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
</symbol>
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
</symbol>
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 4 7 12 15 20"/>
</symbol>
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 4 17 12 9 20"/>
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 13 9.5 18.5 20 6"/>
</symbol>
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
</symbol>
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
</symbol>
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</symbol>
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
</symbol></svg>
<header class="site-header">
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
<div class="site-search">
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
</div>
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
</button>
</header>
<div class="site-body">
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><li data-kapitel="synthese-grundlagen.html"><a href="synthese-grundlagen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><li data-kapitel="synthese-kernverfahren.html"><a href="synthese-kernverfahren.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><details class="sidebar-gruppe" open><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</summary><ul><li data-kapitel="qp-nlp.html"><a href="qp-nlp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a></li><li data-kapitel="unsicherheit.html" class="aktiv"><a href="unsicherheit.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a></li><li data-kapitel="dynamische-programmierung.html"><a href="dynamische-programmierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span></a></li><li data-kapitel="mehrziel.html"><a href="mehrziel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span></a></li><li data-kapitel="prognose.html"><a href="prognose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung</span></a></li><li data-kapitel="synthese-nichtlinear.html"><a href="synthese-nichtlinear.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil III</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte</summary><ul><li data-kapitel="bruecke.html"><a href="bruecke.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten</span></a></li><li data-kapitel="supplychain.html"><a href="supplychain.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit</span></a></li><li data-kapitel="finanzdaten.html"><a href="finanzdaten.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage</span></a></li><li data-kapitel="markowitz.html"><a href="markowitz.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 19: Die moderne Portfoliotheorie nach Markowitz</span></a></li><li data-kapitel="cvar.html"><a href="cvar.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten</span></a></li><li data-kapitel="handelsmaschine.html"><a href="handelsmaschine.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 21: Die vollständige quantitative Handelsmaschine</span></a></li><li data-kapitel="synthese-anwendungen.html"><a href="synthese-anwendungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil IV</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil V: Praxis</summary><ul><li data-kapitel="praxisfallen.html"><a href="praxisfallen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz</span></a></li><li data-kapitel="testing.html"><a href="testing.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 23: Testen, Messen, Ausliefern</span></a></li><li data-kapitel="synthese-praxis.html"><a href="synthese-praxis.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil V</span></a></li><li data-kapitel="projektwerkstatt.html"><a href="projektwerkstatt.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Projektwerkstatt</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Anhänge</summary><ul><li data-kapitel="anhang-loesungen.html"><a href="anhang-loesungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang A: Lösungen zu allen Übungsaufgaben</span></a></li><li data-kapitel="anhang-modellierungsmuster.html"><a href="anhang-modellierungsmuster.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang B: Katalog der Modellierungsmuster</span></a></li><li data-kapitel="anhang-fehlerdiagnose.html"><a href="anhang-fehlerdiagnose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang C: Fehlerdiagnose-Handbuch</span></a></li><li data-kapitel="anhang-spickzettel.html"><a href="anhang-spickzettel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang D: Spickzettel der Solver</span></a></li><li data-kapitel="anhang-glossar-literatur.html"><a href="anhang-glossar-literatur.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang E: Glossar und Literatur</span></a></li></ul></details><ul class="sidebar-extra"><li><a href="programme.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> Beispielprogramme</a></li><li><a href="stichwortverzeichnis.html"><svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg> Stichwortverzeichnis</a></li><li><a href="gesamtdokument.html"><svg class="icon" aria-hidden="true"><use href="#icon-external-link"></use></svg> Gesamtdokument (eine Seite)</a></li><li><a href="Operations_Research_mit_Python_Version_04.pdf" download><svg class="icon" aria-hidden="true"><use href="#icon-download"></use></svg> Download als PDF</a></li></ul></div></nav>
<main class="content">
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> <span>Teil III</span> <span>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></nav>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="qp-nlp.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a><a class="prev-next-knopf prev-next-next" href="dynamische-programmierung.html"><span><small>Weiter</small>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
<article>
<h1 id="kap-unsicherheit">Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</h1>
<div class="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit <strong>Zufallsgrößen</strong>. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.</p>
<p><strong>Voraussetzungen:</strong> <a href="lp.html#kap-lp">Kapitel 5</a>, <a href="qp-nlp.html#kap-qp-nlp">Kapitel 11</a>. Grundbegriffe der Statistik (Erwartungswert, Quantil).</p>
<p><strong>Danach können Sie:</strong> Ein zweistufiges stochastisches Modell aufstellen, Monte-Carlo zur Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht.</p>
<p><strong>Zeitbedarf:</strong> ca. 6,5 Stunden.</p>
<p><strong>Programme:</strong><br />
<code>Fluch_des_Durchschnitts.py</code><br />
<code>Monte_Carlo.py</code><br />
<code>Stochastische_Optimierung.py</code><br />
<code>Robuste_Optimierung.py</code><br />
<code>Chance_Constraints.py</code></p>
<p><strong>Notebook:</strong> <a href="Notebooks_04/unsicherheit.ipynb">unsicherheit.ipynb</a><br />
<a href="https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/unsicherheit.ipynb">In Google Colab öffnen</a></p>
</blockquote>
</div>
<hr />
<h2 id="sec:unsicherheit-schnellstart">12.1 In 5 Minuten gelöst</h2>
<div class="card card-schnellstart">
<blockquote>
<p><strong>🚀 In 5 Minuten gelöst: Wie viele Ersatzteile bestellen?</strong></p>
<p>Ein Betrieb bestellt vor der Wintersaison Ersatzteile — <strong>einmal</strong>, danach ist die Lieferkette bis zum Frühjahr zu. Der Bedarf schwankt, im Mittel werden 30 Stück gebraucht (Poisson-verteilt).</p>
<table>
<colgroup>
<col style="width: 42%" />
<col style="width: 57%" />
</colgroup>
<thead>
<tr class="header">
<th>Fall</th>
<th style="text-align: right;">Kosten je Stück</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Zu viel bestellt</strong> — Teil bleibt liegen und wird abgeschrieben</td>
<td style="text-align: right;">120 €</td>
</tr>
<tr class="even">
<td><strong>Zu wenig bestellt</strong> — Maschine steht bis zur Nachlieferung</td>
<td style="text-align: right;">1 400 €</td>
</tr>
</tbody>
</table>
<p>Wie viele bestellen Sie?</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>KOSTEN_ZUVIEL, KOSTEN_ZUWENIG <span class="op">=</span> <span class="fl">120.0</span>, <span class="fl">1400.0</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>bedarf <span class="op">=</span> np.random.default_rng(<span class="dv">7</span>).poisson(<span class="dv">30</span>, <span class="dv">200_000</span>) <span class="co"># 200.000 Winter</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kosten(menge):</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> zuviel <span class="op">=</span> np.maximum(menge <span class="op">-</span> bedarf, <span class="dv">0</span>)</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> zuwenig <span class="op">=</span> np.maximum(bedarf <span class="op">-</span> menge, <span class="dv">0</span>)</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (KOSTEN_ZUVIEL <span class="op">*</span> zuviel <span class="op">+</span> KOSTEN_ZUWENIG <span class="op">*</span> zuwenig).mean()</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>kandidaten <span class="op">=</span> np.arange(<span class="dv">20</span>, <span class="dv">61</span>)</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>werte <span class="op">=</span> np.array([kosten(m) <span class="cf">for</span> m <span class="kw">in</span> kandidaten])</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Optimum:&quot;</span>, kandidaten[werte.argmin()], <span class="st">&quot;Stueck,&quot;</span>, <span class="ss">f&quot;</span><span class="sc">{</span>werte<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Mittlerer Bedarf 30:&quot;</span>, <span class="ss">f&quot;</span><span class="sc">{</span>kosten(<span class="dv">30</span>)<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>Optimum: 38 Stueck, 1,270 EUR
Mittlerer Bedarf 30: 3,306 EUR</code></pre>
</blockquote>
</div>
<p><strong>Die naheliegende Antwort — „im Mittel brauchen wir 30, also bestellen wir 30“ — kostet das 2,6-Fache.</strong> 3 306 € statt 1 270 €, ein Aufschlag von 160 %.</p>
<p>Der Grund liegt in der <strong>Asymmetrie</strong>: Ein Teil zu viel kostet 120 €, ein Teil zu wenig kostet 1 400 €. Beides ist nicht gleich schlimm, also darf man auch nicht in der Mitte landen. Man muss sich bewusst auf die günstigere Seite des Fehlers stellen.</p>
<p>Wie weit? Dafür gibt es eine geschlossene Formel — das <strong>kritische Verhältnis</strong>:</p>
<p><span class="math display">
q^{*} = \frac{c_{\text{zu wenig}}}{c_{\text{zu wenig}} + c_{\text{zu viel}}}
= \frac{1400}{1400 + 120} = 0{,}921
</span></p>
<div class="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer</strong></p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>Mathematik</th>
<th>Alltagssprache</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><span class="math inline">c_{\text{zu wenig}}</span></td>
<td>„Was kostet mich ein Stück, das ich gebraucht hätte und nicht habe?“</td>
</tr>
<tr class="even">
<td><span class="math inline">c_{\text{zu viel}}</span></td>
<td>„Was kostet mich ein Stück, das ich habe und nicht brauche?“</td>
</tr>
<tr class="odd">
<td><span class="math inline">q^{*} = 0{,}921</span></td>
<td>„Bestelle so viel, dass der Bedarf in <strong>92,1 %</strong> aller Winter gedeckt ist.“</td>
</tr>
<tr class="even">
<td>Bestellmenge <span class="math inline">= F^{-1}(q^{*})</span></td>
<td>„Nimm das 92,1-%-Quantil der Bedarfsverteilung“ — hier 38 Stück.</td>
</tr>
</tbody>
</table>
<p><strong>In einem Satz:</strong> <em>Je teurer der Engpass gegenüber dem Überbestand, desto weiter über den Mittelwert hinaus muss man bestellen.</em></p>
</blockquote>
</div>
<p>Das Quantil zu 0,921 liegt bei genau <strong>38 Stück</strong> — dasselbe Ergebnis, das die Simulation oben gefunden hat. Zwei völlig verschiedene Wege, dieselbe Zahl.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Die optimale Entscheidung unter Unsicherheit ist fast nie die Entscheidung, die für den Mittelwert optimal wäre. Sie hängt davon ab, <strong>welcher der beiden Fehler teurer ist</strong> — und verschiebt sich zu der Seite, auf der Irren billiger ist. Wer mit dem Mittelwert plant, hat diese Frage nie gestellt.</p>
</blockquote>
<p>Dieses Muster heißt <strong>Newsvendor-Problem</strong> (deutsch: Zeitungsjungen-Problem) und taucht überall dort auf, wo einmal entschieden und danach beobachtet wird: Ersatzteile, Frischwaren, Saisonware, Personalreserve, Kraftwerksvorhaltung. Der Rest dieses Kapitels verallgemeinert es — von einer Zahl auf ganze Modelle, und von einer bekannten Verteilung auf den Fall, dass man nicht einmal die kennt.</p>
<hr />
<h2 id="sec:unsicherheit-lernziele">12.2 Lernziele</h2>
<p>Nach diesem Kapitel können Sie …</p>
<ol type="1">
<li>… den <strong>Fluch des Durchschnitts</strong> (<em>Flaw of Averages</em>) an einem Beispiel erklären.</li>
<li>… ein <strong>zweistufiges stochastisches Programm</strong> mit Recourse formulieren.</li>
<li>… Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.</li>
<li>… ein <strong>robustes</strong> Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.</li>
<li>… eine <strong>Wahrscheinlichkeitszusage</strong> („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.</li>
<li>… begründen, welcher der vier Ansätze für ein gegebenes Problem passt.</li>
<li>… das <strong>kritische Verhältnis</strong> eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen.</li>
<li>… eine Terminzusage als Quantil formulieren statt als Mittelwert.</li>
</ol>
<hr />
<h2 id="sec:unsicherheit-der-fluch-des-durchschnitts">12.3 Der Fluch des Durchschnitts</h2>
<p>Der naheliegende Umgang mit Unsicherheit lautet: „Wir setzen den Erwartungswert ein und rechnen deterministisch.“ Das ist <strong>fast immer falsch</strong> — und zwar nicht ungenau, sondern systematisch verzerrt.</p>
<blockquote>
<p><strong>✏️ Handrechnung 12.1: Warum der Mittelwert in die Irre führt</strong></p>
<p>Ein Betreiber braucht Serverkapazität. Der Bedarf ist:</p>
<table>
<thead>
<tr class="header">
<th>Szenario</th>
<th>Wahrscheinlichkeit</th>
<th>Bedarf</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Ruhig</td>
<td>50 %</td>
<td>100</td>
</tr>
<tr class="even">
<td>Volatil</td>
<td>30 %</td>
<td>250</td>
</tr>
<tr class="odd">
<td>Crash</td>
<td>20 %</td>
<td>500</td>
</tr>
</tbody>
</table>
<p><strong>Erwartungswert:</strong> <span class="math inline">0{,}5\cdot100 + 0{,}3\cdot250 + 0{,}2\cdot500 = 50+75+100 = 225</span>.</p>
<p>Kosten: Vorabkauf 40 €/Einheit; fehlende Kapazität muss kurzfristig für 120 € zugekauft werden; überschüssige Kapazität kostet 5 € Verwaltung.</p>
<p><strong>Naive Planung mit <span class="math inline">x = 225</span>:</strong> * Ruhig (Bedarf 100): 125 <strong>zu viel</strong><span class="math inline">40\cdot225 + 5\cdot125 = 9000 + 625 = 9625</span> * Volatil (250): 25 <strong>zu wenig</strong><span class="math inline">9000 + 120\cdot25 = 9000 + 3000 = 12\,000</span> * Crash (500): 275 zu wenig → <span class="math inline">9000 + 120\cdot275 = 9000 + 33\,000 = 42\,000</span> * <strong>Erwartete Kosten:</strong> <span class="math inline">0{,}5\cdot9625 + 0{,}3\cdot12\,000 + 0{,}2\cdot42\,000 = 4812{,}5 + 3600 + 8400 = \mathbf{16\,812{,}50}</span></p>
<p><strong>Stochastisch optimale Planung mit <span class="math inline">x = 250</span>:</strong> * Ruhig: 150 zu viel → <span class="math inline">10\,000 + 750 = 10\,750</span> * Volatil: passt genau → <span class="math inline">10\,000</span> * Crash: 250 zu wenig → <span class="math inline">10\,000 + 30\,000 = 40\,000</span> * <strong>Erwartete Kosten:</strong> <span class="math inline">0{,}5\cdot10\,750 + 0{,}3\cdot10\,000 + 0{,}2\cdot40\,000 = 5375 + 3000 + 8000 = \mathbf{16\,375{,}00}</span></p>
<p><strong>Die naive Planung kostet 437,50 € mehr — rund 2,7 % Aufschlag</strong> für das Einsetzen eines Mittelwerts, den es als Szenario gar nicht gibt. Beachten Sie: Der Aufschlag wirkt klein, weil bei nur drei Szenarien 225 und 250 nahe beieinanderliegen. Entscheidend ist die <strong>Richtung</strong>: Das Optimum liegt systematisch <strong>über</strong> dem Mittelwert, und zwar bei genau jenem Szenariowert, an dem die Kostenbalance kippt.</p>
</blockquote>
<p>Dass <span class="math inline">x = 250</span> tatsächlich das Minimum ist und nicht nur eine plausible Vermutung, zeigt ein vollständiger Scan über alle möglichen Kapazitäten:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Fluch_des_Durchschnitts.py</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Unsicherheit: Der Fluch des Durchschnitts (die Handrechnung dazu) als Scan ueber alle</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">moeglichen Kapazitaeten - zeigt, dass das Optimum nicht beim Mittelwert liegt.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>SZENARIEN <span class="op">=</span> np.array([<span class="dv">100</span>, <span class="dv">250</span>, <span class="dv">500</span>])</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>WAHRSCHEINLICHKEITEN <span class="op">=</span> np.array([<span class="fl">0.5</span>, <span class="fl">0.3</span>, <span class="fl">0.2</span>])</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>PREIS_VORAB <span class="op">=</span> <span class="fl">40.0</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>PREIS_ZUKAUF <span class="op">=</span> <span class="fl">120.0</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>PREIS_VERWALTUNG <span class="op">=</span> <span class="fl">5.0</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>ERWARTUNGSWERT <span class="op">=</span> <span class="bu">float</span>(SZENARIEN <span class="op">@</span> WAHRSCHEINLICHKEITEN)</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> erwartete_kosten(x):</span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> np.where(</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> SZENARIEN <span class="op">&gt;=</span> x,</span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> PREIS_VORAB <span class="op">*</span> x <span class="op">+</span> PREIS_ZUKAUF <span class="op">*</span> (SZENARIEN <span class="op">-</span> x),</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> PREIS_VORAB <span class="op">*</span> x <span class="op">+</span> PREIS_VERWALTUNG <span class="op">*</span> (x <span class="op">-</span> SZENARIEN),</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(kosten <span class="op">@</span> WAHRSCHEINLICHKEITEN)</span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; DER FLUCH DES DURCHSCHNITTS&quot;</span>)</span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Erwarteter Bedarf (Mittelwert): </span><span class="sc">{</span>ERWARTUNGSWERT<span class="sc">:.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Kosten bei naiver Planung x=</span><span class="sc">{</span>ERWARTUNGSWERT<span class="sc">:.0f}</span><span class="ss">: &quot;</span></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>erwartete_kosten(ERWARTUNGSWERT)<span class="sc">:,.2f}</span><span class="ss"> EUR</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> x_werte <span class="op">=</span> np.arange(<span class="dv">0</span>, <span class="dv">501</span>, <span class="dv">1</span>)</span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a> kosten_werte <span class="op">=</span> np.array([erwartete_kosten(x) <span class="cf">for</span> x <span class="kw">in</span> x_werte])</span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a> x_optimal <span class="op">=</span> x_werte[np.argmin(kosten_werte)]</span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a> kosten_optimal <span class="op">=</span> kosten_werte.<span class="bu">min</span>()</span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Optimales x (durch Scan gefunden): </span><span class="sc">{</span>x_optimal<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Kosten beim Optimum: </span><span class="sc">{</span>kosten_optimal<span class="sc">:,.2f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Ersparnis gegenueber naiver Planung: &quot;</span></span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>erwartete_kosten(ERWARTUNGSWERT) <span class="op">-</span> kosten_optimal<span class="sc">:,.2f}</span><span class="ss"> EUR</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Kosten fuer ausgewaehlte x zum Vergleich:&quot;</span>)</span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> x <span class="kw">in</span> [<span class="dv">100</span>, <span class="dv">225</span>, <span class="dv">250</span>, <span class="dv">300</span>, <span class="dv">500</span>]:</span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a> markierung <span class="op">=</span> <span class="st">&quot; &lt;- Mittelwert&quot;</span> <span class="cf">if</span> x <span class="op">==</span> <span class="dv">225</span> <span class="cf">else</span> (<span class="st">&quot; &lt;- Optimum&quot;</span> <span class="cf">if</span> x <span class="op">==</span> <span class="dv">250</span> <span class="cf">else</span> <span class="st">&quot;&quot;</span>)</span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; x=</span><span class="sc">{</span>x<span class="sc">:4d}</span><span class="ss">: </span><span class="sc">{</span>erwartete_kosten(x)<span class="sc">:&gt;12,.2f}</span><span class="ss"> EUR</span><span class="sc">{</span>markierung<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Das Optimum liegt exakt auf einem Szenariowert (250 = &#39;Volatil&#39;),&quot;</span>)</span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.&quot;</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe:</strong></p>
<pre><code>======================================================================
DER FLUCH DES DURCHSCHNITTS
======================================================================
Erwarteter Bedarf (Mittelwert): 225.0
Kosten bei naiver Planung x=225: 16,812.50 EUR
Optimales x (durch Scan gefunden): 250
Kosten beim Optimum: 16,375.00 EUR
Ersparnis gegenueber naiver Planung: 437.50 EUR
Kosten fuer ausgewaehlte x zum Vergleich:
x= 100: 19,000.00 EUR
x= 225: 16,812.50 EUR &lt;- Mittelwert
x= 250: 16,375.00 EUR &lt;- Optimum
x= 300: 17,375.00 EUR
x= 500: 21,375.00 EUR
Das Optimum liegt exakt auf einem Szenariowert (250 = &#39;Volatil&#39;),
nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.</code></pre>
<p>Kein Rechentrick, sondern brute-force über alle ganzzahligen <span class="math inline">x</span> von 0 bis 500 — das Optimum landet exakt dort, wo es die Handrechnung vorhersagt.</p>
<p><strong>Warum?</strong> Die Kostenfunktion ist <strong>asymmetrisch</strong>: Unterdeckung kostet 120, Überdeckung nur 5. Bei asymmetrischen Kosten liegt das Optimum nie beim Mittelwert, sondern verschoben in die „billigere“ Richtung. Formal ist das die <strong>Jensensche Ungleichung</strong>: Für konvexe Kostenfunktionen gilt <span class="math inline">\mathbb{E}[f(X)] \ge f(\mathbb{E}[X])</span> — der Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt.</p>
</blockquote>
<h3 id="die-vier-ansätze-im-überblick">Die vier Ansätze im Überblick</h3>
<figure>
<img src="bilder_04/kap09_unsicherheit_ansaetze.svg" alt="Abb. 12.1: Ansätze für Optimierung unter Unsicherheit" />
<figcaption aria-hidden="true">Abb. 12.1: Ansätze für Optimierung unter Unsicherheit</figcaption>
</figure>
<table>
<colgroup>
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
</colgroup>
<thead>
<tr class="header">
<th></th>
<th><strong>Monte-Carlo</strong></th>
<th><strong>Stochastische Programmierung</strong></th>
<th><strong>Robuste Optimierung</strong></th>
<th><strong>Chance Constraints</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Braucht</td>
<td>Verteilung</td>
<td>Szenarien <strong>mit Wahrscheinlichkeiten</strong></td>
<td>nur eine <strong>Menge</strong> möglicher Werte</td>
<td>Verteilung <strong>oder</strong> Szenarien</td>
</tr>
<tr class="even">
<td>Optimiert</td>
<td>nichts (bewertet nur)</td>
<td><strong>Erwartungswert</strong> über Szenarien</td>
<td><strong>Worst Case</strong> in der Menge</td>
<td>Kosten bei <strong>zugesicherter Quote</strong></td>
</tr>
<tr class="odd">
<td>Typische Frage</td>
<td>„Wie riskant ist dieser Plan?“</td>
<td>„Was ist im Mittel am besten?“</td>
<td>„Was hält auch im schlimmsten Fall?“</td>
<td>„Was hält in 95 % der Fälle?“</td>
</tr>
<tr class="even">
<td>Stärke</td>
<td>beliebige Kennzahlen, sehr flexibel</td>
<td>nutzt Wahrscheinlichkeiten voll aus</td>
<td>keine Verteilungsannahme nötig</td>
<td>die Zusage steht <strong>im Modell</strong></td>
</tr>
<tr class="odd">
<td>Schwäche</td>
<td>keine Optimierung</td>
<td>Wahrscheinlichkeiten müssen stimmen</td>
<td>oft übervorsichtig, kostet Ertrag</td>
<td>Zusage gilt nur für die unterstellte Verteilung</td>
</tr>
<tr class="even">
<td>Nachzulesen in</td>
<td><a href="#sec:unsicherheit-monte-carlo-simulation">Abschnitt 12.4</a></td>
<td><a href="#sec:unsicherheit-zweistufige-stochastische-programmierung">Abschnitt 12.5</a></td>
<td><a href="#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case">Abschnitt 12.6</a></td>
<td><a href="#sec:unsicherheit-chance-constraints">Abschnitt 12.7</a></td>
</tr>
</tbody>
</table>
<p><strong>Der Unterschied zwischen den letzten beiden</strong> ist der, an dem die meisten Diskussionen hängenbleiben. Die robuste Optimierung sagt: <em>„Es darf <strong>nie</strong> schiefgehen“</em> — und zahlt dafür. Die Chance Constraint sagt: <em>„Es darf in höchstens 5 % der Fälle schiefgehen“</em> — und macht damit verhandelbar, was die Sicherheit kosten darf.</p>
<hr />
<h2 id="sec:unsicherheit-monte-carlo-simulation">12.4 Monte-Carlo-Simulation</h2>
<p><strong>Monte-Carlo</strong> heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan aus, und betrachte die <strong>Verteilung</strong> der Ergebnisse statt eines einzigen Werts.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Monte_Carlo.py</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Unsicherheit: Monte-Carlo-Bewertung von Kapazitaetsplaenen.</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co">Monte Carlo OPTIMIERT nicht - es BEWERTET. Der Nutzen liegt darin, dass man</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co">beliebige Kennzahlen ablesen kann: Erwartungswert, Quantile, Ausfallwahr-</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co">scheinlichkeit, Worst Case. Genau diese Groessen braucht man, um zwischen</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co">Plaenen zu entscheiden.</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>KOSTEN_VORAB <span class="op">=</span> <span class="fl">40.0</span> <span class="co"># EUR je Einheit, im Voraus gekauft</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>KOSTEN_SPOT <span class="op">=</span> <span class="fl">120.0</span> <span class="co"># EUR je Einheit, kurzfristig zugekauft</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>KOSTEN_LEERLAUF <span class="op">=</span> <span class="fl">5.0</span> <span class="co"># EUR je ungenutzter Einheit</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>ANZAHL_ZIEHUNGEN <span class="op">=</span> <span class="dv">100_000</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> ziehe_bedarf(rng, ziehungen):</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="co"> Bedarfsmodell: Mischverteilung aus Normalbetrieb und seltenen Lastspitzen.</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="co"> Realistischer als eine reine Normalverteilung - Krisen sind selten,</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a><span class="co"> aber extrem (fat tail).</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a> normal <span class="op">=</span> rng.normal(loc<span class="op">=</span><span class="dv">150</span>, scale<span class="op">=</span><span class="dv">40</span>, size<span class="op">=</span>ziehungen)</span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a> spitze <span class="op">=</span> rng.normal(loc<span class="op">=</span><span class="dv">450</span>, scale<span class="op">=</span><span class="dv">80</span>, size<span class="op">=</span>ziehungen)</span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a> ist_spitze <span class="op">=</span> rng.random(ziehungen) <span class="op">&lt;</span> <span class="fl">0.15</span> <span class="co"># 15 % Lastspitzen</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> np.maximum(np.where(ist_spitze, spitze, normal), <span class="fl">0.0</span>)</span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kosten_fuer(kapazitaet, bedarf):</span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Gesamtkosten je Szenario fuer eine gegebene Vorabkapazitaet.&quot;&quot;&quot;</span></span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a> unterdeckung <span class="op">=</span> np.maximum(bedarf <span class="op">-</span> kapazitaet, <span class="fl">0.0</span>)</span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a> ueberdeckung <span class="op">=</span> np.maximum(kapazitaet <span class="op">-</span> bedarf, <span class="fl">0.0</span>)</span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (KOSTEN_VORAB <span class="op">*</span> kapazitaet</span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> KOSTEN_SPOT <span class="op">*</span> unterdeckung</span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> KOSTEN_LEERLAUF <span class="op">*</span> ueberdeckung)</span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a> rng <span class="op">=</span> np.random.default_rng(<span class="dv">2026</span>)</span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a> bedarf <span class="op">=</span> ziehe_bedarf(rng, ANZAHL_ZIEHUNGEN)</span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; MONTE-CARLO-BEWERTUNG (</span><span class="sc">{</span>ANZAHL_ZIEHUNGEN<span class="sc">:,}</span><span class="ss"> Szenarien)&quot;</span>)</span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Bedarfsverteilung: Mittelwert </span><span class="sc">{</span>bedarf<span class="sc">.</span>mean()<span class="sc">:.1f}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Median </span><span class="sc">{</span>np<span class="sc">.</span>median(bedarf)<span class="sc">:.1f}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;95%-Quantil </span><span class="sc">{</span>np<span class="sc">.</span>percentile(bedarf, <span class="dv">95</span>)<span class="sc">:.1f}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Maximum </span><span class="sc">{</span>bedarf<span class="sc">.</span><span class="bu">max</span>()<span class="sc">:.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Der Median liegt deutlich unter dem Mittelwert - die Verteilung ist&quot;</span>)</span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;rechtsschief. Genau hier fuehrt Planung mit dem Mittelwert in die Irre.</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Kapazitaet&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Erw. Kosten&#39;</span><span class="sc">:&gt;12}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Median&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;95%-Quantil&#39;</span><span class="sc">:&gt;12}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Unterdeckung&#39;</span><span class="sc">:&gt;12}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true" tabindex="-1"></a> kandidaten <span class="op">=</span> [<span class="dv">150</span>, <span class="dv">200</span>, <span class="dv">225</span>, <span class="dv">250</span>, <span class="dv">300</span>, <span class="dv">350</span>, <span class="dv">400</span>]</span>
<span id="cb5-62"><a href="#cb5-62" aria-hidden="true" tabindex="-1"></a> ergebnisse <span class="op">=</span> []</span>
<span id="cb5-63"><a href="#cb5-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> kapazitaet <span class="kw">in</span> kandidaten:</span>
<span id="cb5-64"><a href="#cb5-64" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> kosten_fuer(kapazitaet, bedarf)</span>
<span id="cb5-65"><a href="#cb5-65" aria-hidden="true" tabindex="-1"></a> p_unterdeckung <span class="op">=</span> <span class="bu">float</span>(np.mean(bedarf <span class="op">&gt;</span> kapazitaet))</span>
<span id="cb5-66"><a href="#cb5-66" aria-hidden="true" tabindex="-1"></a> ergebnisse.append((kapazitaet, kosten.mean(), p_unterdeckung))</span>
<span id="cb5-67"><a href="#cb5-67" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>kapazitaet<span class="sc">:&gt;10}</span><span class="ss"> | </span><span class="sc">{</span>kosten<span class="sc">.</span>mean()<span class="sc">:&gt;12,.0f}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>np<span class="sc">.</span>median(kosten)<span class="sc">:&gt;10,.0f}</span><span class="ss"> | </span><span class="sc">{</span>np<span class="sc">.</span>percentile(kosten, <span class="dv">95</span>)<span class="sc">:&gt;12,.0f}</span><span class="ss"> | &quot;</span></span>
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>p_unterdeckung<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;11.1f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb5-70"><a href="#cb5-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-71"><a href="#cb5-71" aria-hidden="true" tabindex="-1"></a> beste <span class="op">=</span> <span class="bu">min</span>(ergebnisse, key<span class="op">=</span><span class="kw">lambda</span> t: t[<span class="dv">1</span>])</span>
<span id="cb5-72"><a href="#cb5-72" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb5-73"><a href="#cb5-73" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Bester Kandidat: Kapazitaet </span><span class="sc">{</span>beste[<span class="dv">0</span>]<span class="sc">}</span><span class="ss"> mit erwarteten Kosten &quot;</span></span>
<span id="cb5-74"><a href="#cb5-74" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>beste[<span class="dv">1</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb5-75"><a href="#cb5-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-76"><a href="#cb5-76" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Feinsuche ueber ein Raster ---------------------------------------</span></span>
<span id="cb5-77"><a href="#cb5-77" aria-hidden="true" tabindex="-1"></a> raster <span class="op">=</span> np.arange(<span class="dv">100</span>, <span class="dv">500</span>, <span class="dv">5</span>)</span>
<span id="cb5-78"><a href="#cb5-78" aria-hidden="true" tabindex="-1"></a> erwartete <span class="op">=</span> np.array([kosten_fuer(k, bedarf).mean() <span class="cf">for</span> k <span class="kw">in</span> raster])</span>
<span id="cb5-79"><a href="#cb5-79" aria-hidden="true" tabindex="-1"></a> optimum <span class="op">=</span> raster[<span class="bu">int</span>(np.argmin(erwartete))]</span>
<span id="cb5-80"><a href="#cb5-80" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Feinsuche (Raster 100..500): Optimum bei Kapazitaet </span><span class="sc">{</span>optimum<span class="sc">}</span><span class="ss">, &quot;</span></span>
<span id="cb5-81"><a href="#cb5-81" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Kosten </span><span class="sc">{</span>erwartete<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb5-82"><a href="#cb5-82" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-83"><a href="#cb5-83" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vergleich mit der naiven Mittelwertplanung ----------------------</span></span>
<span id="cb5-84"><a href="#cb5-84" aria-hidden="true" tabindex="-1"></a> naiv <span class="op">=</span> <span class="bu">int</span>(<span class="bu">round</span>(bedarf.mean()))</span>
<span id="cb5-85"><a href="#cb5-85" aria-hidden="true" tabindex="-1"></a> kosten_naiv <span class="op">=</span> kosten_fuer(naiv, bedarf).mean()</span>
<span id="cb5-86"><a href="#cb5-86" aria-hidden="true" tabindex="-1"></a> kosten_opt <span class="op">=</span> kosten_fuer(optimum, bedarf).mean()</span>
<span id="cb5-87"><a href="#cb5-87" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">--- Fluch des Durchschnitts, gemessen ---&quot;</span>)</span>
<span id="cb5-88"><a href="#cb5-88" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Planung mit Mittelwert (</span><span class="sc">{</span>naiv<span class="sc">}</span><span class="ss">): </span><span class="sc">{</span>kosten_naiv<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb5-89"><a href="#cb5-89" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Monte-Carlo-Optimum (</span><span class="sc">{</span>optimum<span class="sc">}</span><span class="ss">): </span><span class="sc">{</span>kosten_opt<span class="sc">:,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb5-90"><a href="#cb5-90" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Mehrkosten der naiven Planung: </span><span class="sc">{</span>kosten_naiv <span class="op">-</span> kosten_opt<span class="sc">:,.0f}</span><span class="ss"> EUR &quot;</span></span>
<span id="cb5-91"><a href="#cb5-91" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(</span><span class="sc">{</span>(kosten_naiv<span class="op">/</span>kosten_opt <span class="op">-</span> <span class="dv">1</span>)<span class="op">*</span><span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> %)&quot;</span>)</span>
<span id="cb5-92"><a href="#cb5-92" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe (gekürzt):</strong></p>
<pre><code>========================================================================================
MONTE-CARLO-BEWERTUNG (100,000 Szenarien)
========================================================================================
Bedarfsverteilung: Mittelwert 195.7 | Median 159.2 | 95%-Quantil 485.2 | Maximum 753.3
Der Median liegt deutlich unter dem Mittelwert - die Verteilung ist
rechtsschief. Genau hier fuehrt Planung mit dem Mittelwert in die Irre.
Kapazitaet | Erw. Kosten | Median | 95%-Quantil | Unterdeckung
----------------------------------------------------------------------------------------
150 | 13,169 | 7,104 | 46,220 | 57.8 %
200 | 12,989 | 8,345 | 42,220 | 24.1 %
225 | 13,475 | 9,432 | 40,220 | 17.8 %
250 | 14,085 | 10,546 | 38,220 | 15.6 %
300 | 15,391 | 12,791 | 34,220 | 14.8 %
350 | 16,746 | 15,033 | 30,220 | 13.6 %
400 | 18,214 | 17,266 | 26,220 | 11.1 %
----------------------------------------------------------------------------------------
Bester Kandidat: Kapazitaet 200 mit erwarteten Kosten 12,989 EUR
Feinsuche (Raster 100..500): Optimum bei Kapazitaet 180, Kosten 12,810 EUR
--- Fluch des Durchschnitts, gemessen ---
Planung mit Mittelwert (196): 12,934 EUR
Monte-Carlo-Optimum (180): 12,810 EUR
Mehrkosten der naiven Planung: 124 EUR (1.0 %)
========================================================================================</code></pre>
<blockquote>
<p><strong>🎯 Eine wichtige Einschränkung — und was man daraus lernt</strong></p>
<p>Hier kostet die naive Mittelwertplanung nur <strong>1 %</strong>. In der Handrechnung zum Fluch des Durchschnitts waren es 2,7 %, in Lehrbüchern liest man von 20 % und mehr. <strong>Wie groß der Effekt ist, hängt von der Verteilung ab</strong> — und das ist selbst die Lektion.</p>
<p>Bei dieser Mischverteilung liegt zwischen dem Normalbetrieb (um 150) und den Lastspitzen (um 450) ein Bereich, in dem kaum Wahrscheinlichkeitsmasse liegt. Zusätzliche Kapazität zwischen 200 und 400 nützt daher wenig: Sie ist im Normalfall Leerlauf und reicht im Krisenfall trotzdem nicht. Deshalb ist die Kostenkurve dort flach, und die Wahl zwischen 180 und 196 macht kaum einen Unterschied.</p>
<p><strong>Was Sie mitnehmen sollten:</strong> Die Frage „Wie teuer ist Mittelwertplanung?“ hat keine allgemeine Antwort — <strong>rechnen Sie es aus.</strong> Genau dafür ist Monte-Carlo da. Und beachten Sie die Spalte „Unterdeckung“: Bei Kapazität 200 wird in <strong>24 % der Fälle</strong> nachgekauft. Ob das akzeptabel ist, entscheidet kein Optimierer, sondern ein Servicelevel-Ziel (Aufgabe <em>Monte-Carlo erweitern</em>, <a href="#sec:unsicherheit-uebungsaufgaben">Abschnitt 12.8</a>).</p>
</blockquote>
<blockquote>
<p><strong>💡 Wann Monte-Carlo das richtige Werkzeug ist</strong> Immer dann, wenn Sie <strong>eine Kennzahl brauchen, die keine Formel hat</strong>: die Wahrscheinlichkeit, ein Servicelevel zu verfehlen; der erwartete Verlust in den schlechtesten 5 % der Fälle; die Verteilung der Projektdauer. Der Preis ist, dass Monte-Carlo nur <strong>bewertet</strong> — die Optimierung müssen Sie durch Rastersuche oder ein eigenes Modell ergänzen.</p>
</blockquote>
<hr />
<h2 id="sec:unsicherheit-zweistufige-stochastische-programmierung">12.5 Zweistufige stochastische Programmierung</h2>
<p>Das <strong>zweistufige Modell mit Recourse</strong> bildet ab, dass Entscheidungen zeitlich gestaffelt sind:</p>
<ul>
<li><strong>Stufe 1 (<em>here and now</em>, <span class="math inline">\mathbf{x}</span>):</strong> muss <strong>jetzt</strong> getroffen werden, bevor der Zufall sich zeigt — Serverkapazität kaufen, Produktion planen, Portfolio aufsetzen.</li>
<li><strong>Stufe 2 (<em>wait and see</em>, <span class="math inline">\mathbf{y}_s</span>):</strong> Korrekturmaßnahmen, <strong>nachdem</strong> Szenario <span class="math inline">s</span> eingetreten ist — Spot-Zukauf, Überstunden, Notverkauf.</li>
</ul>
<p><span class="math display">
\min_{\mathbf{x}} \quad \mathbf{c}^\top\mathbf{x} \;+\; \sum_{s=1}^S p_s\,\mathbf{q}_s^\top\mathbf{y}_s
</span></p>
<p><span class="math display">
\begin{aligned}
\mathbf{A}\mathbf{x} &amp;\le \mathbf{b} &amp;&amp; \text{(Stufe-1-Restriktionen)}\\
\mathbf{T}_s\mathbf{x} + \mathbf{W}_s\mathbf{y}_s &amp;\le \mathbf{h}_s \quad \forall s
&amp;&amp; \text{(Kopplung je Szenario)}\\
\mathbf{x} \ge \mathbf{0},\ \mathbf{y}_s &amp;\ge \mathbf{0} \quad \forall s
\end{aligned}
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <span class="math inline">\mathbf{c}^\top\mathbf{x}</span> — die Kosten der Vorabentscheidung. Sie fallen <strong>immer</strong> an. * <span class="math inline">p_s</span> — Wahrscheinlichkeit von Szenario <span class="math inline">s</span>; <span class="math inline">\sum_s p_s = 1</span>. * <span class="math inline">\mathbf{q}_s^\top\mathbf{y}_s</span> — die Korrekturkosten <strong>in</strong> Szenario <span class="math inline">s</span>. * <span class="math inline">\mathbf{T}_s\mathbf{x} + \mathbf{W}_s\mathbf{y}_s \le \mathbf{h}_s</span> — die <strong>Kopplungsbedingung</strong>: Sie verbindet die Vorabentscheidung mit dem, was danach möglich ist. Genau hier steckt die Modellierungsarbeit.</p>
<p><strong>Ohne Formel gesagt:</strong> „Entscheide jetzt so, dass die Summe aus heutigen Kosten und <strong>durchschnittlich zu erwartenden</strong> Nachbesserungskosten minimal wird.“</p>
<p><strong>Der entscheidende Punkt:</strong> Es gibt <strong>ein</strong> <span class="math inline">\mathbf{x}</span>, aber <strong>je Szenario ein eigenes</strong> <span class="math inline">\mathbf{y}_s</span>. Das ist keine Rechentrickserei, sondern bildet die Realität ab: Man legt sich einmal fest und reagiert dann unterschiedlich.</p>
</blockquote>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Stochastische_Optimierung.py</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Unsicherheit: Two-Stage Stochastic Programming mit CVXPY.</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="co">Eigenschaften:</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="co"> * Die Analyse am Ende wird BERECHNET statt fest verdrahtet</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="co"> * Vergleich gegen drei Alternativen: Mittelwert, Worst Case, perfekte Voraussicht</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="co"> * Kennzahl EVPI (Wert perfekter Information) wird ausgewiesen</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>SZENARIEN <span class="op">=</span> [<span class="st">&quot;Ruhig&quot;</span>, <span class="st">&quot;Volatil&quot;</span>, <span class="st">&quot;Crash&quot;</span>]</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>WAHRSCHEINLICHKEIT <span class="op">=</span> np.array([<span class="fl">0.50</span>, <span class="fl">0.30</span>, <span class="fl">0.20</span>])</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>BEDARF <span class="op">=</span> np.array([<span class="fl">100.0</span>, <span class="fl">250.0</span>, <span class="fl">500.0</span>])</span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a>KOSTEN_VORAB <span class="op">=</span> <span class="fl">40.0</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a>KOSTEN_SPOT <span class="op">=</span> <span class="fl">120.0</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a>KOSTEN_LEERLAUF <span class="op">=</span> <span class="fl">5.0</span></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a>S <span class="op">=</span> <span class="bu">len</span>(SZENARIEN)</span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_stochastisch():</span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Zweistufiges Modell: eine Vorabentscheidung, szenarioabhaengige Korrektur.&quot;&quot;&quot;</span></span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable(nonneg<span class="op">=</span><span class="va">True</span>, name<span class="op">=</span><span class="st">&quot;Basiskapazitaet&quot;</span>) <span class="co"># Stufe 1</span></span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true" tabindex="-1"></a> y_spot <span class="op">=</span> cp.Variable(S, nonneg<span class="op">=</span><span class="va">True</span>, name<span class="op">=</span><span class="st">&quot;Spot_Zukauf&quot;</span>) <span class="co"># Stufe 2</span></span>
<span id="cb7-32"><a href="#cb7-32" aria-hidden="true" tabindex="-1"></a> y_leer <span class="op">=</span> cp.Variable(S, nonneg<span class="op">=</span><span class="va">True</span>, name<span class="op">=</span><span class="st">&quot;Leerlauf&quot;</span>) <span class="co"># Stufe 2</span></span>
<span id="cb7-33"><a href="#cb7-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-34"><a href="#cb7-34" aria-hidden="true" tabindex="-1"></a> <span class="co"># Kopplung: Basis + Zukauf - Leerlauf == Bedarf (je Szenario)</span></span>
<span id="cb7-35"><a href="#cb7-35" aria-hidden="true" tabindex="-1"></a> nebenbedingungen <span class="op">=</span> [x <span class="op">+</span> y_spot[s] <span class="op">-</span> y_leer[s] <span class="op">==</span> BEDARF[s] <span class="cf">for</span> s <span class="kw">in</span> <span class="bu">range</span>(S)]</span>
<span id="cb7-36"><a href="#cb7-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-37"><a href="#cb7-37" aria-hidden="true" tabindex="-1"></a> erwartete_korrektur <span class="op">=</span> <span class="bu">sum</span>(</span>
<span id="cb7-38"><a href="#cb7-38" aria-hidden="true" tabindex="-1"></a> WAHRSCHEINLICHKEIT[s] <span class="op">*</span> (KOSTEN_SPOT <span class="op">*</span> y_spot[s] <span class="op">+</span> KOSTEN_LEERLAUF <span class="op">*</span> y_leer[s])</span>
<span id="cb7-39"><a href="#cb7-39" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> <span class="bu">range</span>(S))</span>
<span id="cb7-40"><a href="#cb7-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-41"><a href="#cb7-41" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Minimize(KOSTEN_VORAB <span class="op">*</span> x <span class="op">+</span> erwartete_korrektur),</span>
<span id="cb7-42"><a href="#cb7-42" aria-hidden="true" tabindex="-1"></a> nebenbedingungen)</span>
<span id="cb7-43"><a href="#cb7-43" aria-hidden="true" tabindex="-1"></a> problem.solve()</span>
<span id="cb7-44"><a href="#cb7-44" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> problem, x, y_spot, y_leer</span>
<span id="cb7-45"><a href="#cb7-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-46"><a href="#cb7-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-47"><a href="#cb7-47" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kosten_bei(kapazitaet):</span>
<span id="cb7-48"><a href="#cb7-48" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Erwartete Gesamtkosten fuer eine fest vorgegebene Kapazitaet.&quot;&quot;&quot;</span></span>
<span id="cb7-49"><a href="#cb7-49" aria-hidden="true" tabindex="-1"></a> unter <span class="op">=</span> np.maximum(BEDARF <span class="op">-</span> kapazitaet, <span class="fl">0.0</span>)</span>
<span id="cb7-50"><a href="#cb7-50" aria-hidden="true" tabindex="-1"></a> ueber <span class="op">=</span> np.maximum(kapazitaet <span class="op">-</span> BEDARF, <span class="fl">0.0</span>)</span>
<span id="cb7-51"><a href="#cb7-51" aria-hidden="true" tabindex="-1"></a> je_szenario <span class="op">=</span> KOSTEN_VORAB <span class="op">*</span> kapazitaet <span class="op">+</span> KOSTEN_SPOT <span class="op">*</span> unter <span class="op">+</span> KOSTEN_LEERLAUF <span class="op">*</span> ueber</span>
<span id="cb7-52"><a href="#cb7-52" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(WAHRSCHEINLICHKEIT <span class="op">@</span> je_szenario)</span>
<span id="cb7-53"><a href="#cb7-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-54"><a href="#cb7-54" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-55"><a href="#cb7-55" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb7-56"><a href="#cb7-56" aria-hidden="true" tabindex="-1"></a> problem, x, y_spot, y_leer <span class="op">=</span> loese_stochastisch()</span>
<span id="cb7-57"><a href="#cb7-57" aria-hidden="true" tabindex="-1"></a> kapazitaet <span class="op">=</span> <span class="bu">float</span>(x.value)</span>
<span id="cb7-58"><a href="#cb7-58" aria-hidden="true" tabindex="-1"></a> mittelwert <span class="op">=</span> <span class="bu">float</span>(WAHRSCHEINLICHKEIT <span class="op">@</span> BEDARF)</span>
<span id="cb7-59"><a href="#cb7-59" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-60"><a href="#cb7-60" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-61"><a href="#cb7-61" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; STOCHASTISCHE TWO-STAGE OPTIMIERUNG (KAPAZITAETSPLANUNG)&quot;</span>)</span>
<span id="cb7-62"><a href="#cb7-62" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-63"><a href="#cb7-63" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Status: </span><span class="sc">{</span>problem<span class="sc">.</span>status<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb7-64"><a href="#cb7-64" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Erwarteter Bedarf (Mittelwert): </span><span class="sc">{</span>mittelwert<span class="sc">:.1f}</span><span class="ss"> Einheiten&quot;</span>)</span>
<span id="cb7-65"><a href="#cb7-65" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Optimale Stufe-1-Kapazitaet x*: </span><span class="sc">{</span>kapazitaet<span class="sc">:.1f}</span><span class="ss"> Einheiten&quot;</span>)</span>
<span id="cb7-66"><a href="#cb7-66" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Minimale erwartete Gesamtkosten: </span><span class="sc">{</span>problem<span class="sc">.</span>value<span class="sc">:,.2f}</span><span class="ss"> EUR</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb7-67"><a href="#cb7-67" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-68"><a href="#cb7-68" aria-hidden="true" tabindex="-1"></a> tabelle <span class="op">=</span> pd.DataFrame({</span>
<span id="cb7-69"><a href="#cb7-69" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Szenario&quot;</span>: SZENARIEN,</span>
<span id="cb7-70"><a href="#cb7-70" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Wahrsch.&quot;</span>: [<span class="ss">f&quot;</span><span class="sc">{</span>p<span class="op">*</span><span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %&quot;</span> <span class="cf">for</span> p <span class="kw">in</span> WAHRSCHEINLICHKEIT],</span>
<span id="cb7-71"><a href="#cb7-71" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Bedarf&quot;</span>: BEDARF,</span>
<span id="cb7-72"><a href="#cb7-72" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Basis genutzt&quot;</span>: [<span class="bu">min</span>(kapazitaet, b) <span class="cf">for</span> b <span class="kw">in</span> BEDARF],</span>
<span id="cb7-73"><a href="#cb7-73" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Spot-Zukauf&quot;</span>: np.<span class="bu">round</span>(y_spot.value, <span class="dv">1</span>),</span>
<span id="cb7-74"><a href="#cb7-74" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Leerlauf&quot;</span>: np.<span class="bu">round</span>(y_leer.value, <span class="dv">1</span>),</span>
<span id="cb7-75"><a href="#cb7-75" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Kosten (EUR)&quot;</span>: [<span class="ss">f&quot;</span><span class="sc">{</span>KOSTEN_VORAB<span class="op">*</span>kapazitaet <span class="op">+</span> KOSTEN_SPOT<span class="op">*</span>y_spot<span class="sc">.</span>value[s] <span class="op">+</span> KOSTEN_LEERLAUF<span class="op">*</span>y_leer<span class="sc">.</span>value[s]<span class="sc">:,.0f}</span><span class="ss">&quot;</span></span>
<span id="cb7-76"><a href="#cb7-76" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> s <span class="kw">in</span> <span class="bu">range</span>(S)],</span>
<span id="cb7-77"><a href="#cb7-77" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb7-78"><a href="#cb7-78" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(tabelle.to_string(index<span class="op">=</span><span class="va">False</span>))</span>
<span id="cb7-79"><a href="#cb7-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-80"><a href="#cb7-80" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vergleich mit Alternativstrategien (berechnet, nicht behauptet) --</span></span>
<span id="cb7-81"><a href="#cb7-81" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-82"><a href="#cb7-82" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Vergleich verschiedener Planungsstrategien:&quot;</span>)</span>
<span id="cb7-83"><a href="#cb7-83" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Strategie&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Kapazitaet&#39;</span><span class="sc">:&gt;11}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Erw. Kosten&#39;</span><span class="sc">:&gt;14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Mehrkosten&#39;</span><span class="sc">:&gt;13}</span><span class="ss">&quot;</span>)</span>
<span id="cb7-84"><a href="#cb7-84" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-85"><a href="#cb7-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-86"><a href="#cb7-86" aria-hidden="true" tabindex="-1"></a> optimal <span class="op">=</span> problem.value</span>
<span id="cb7-87"><a href="#cb7-87" aria-hidden="true" tabindex="-1"></a> strategien <span class="op">=</span> [</span>
<span id="cb7-88"><a href="#cb7-88" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Stochastisch optimal&quot;</span>, kapazitaet),</span>
<span id="cb7-89"><a href="#cb7-89" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Naiv: Mittelwert einsetzen&quot;</span>, mittelwert),</span>
<span id="cb7-90"><a href="#cb7-90" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Vorsichtig: Worst Case abdecken&quot;</span>, <span class="bu">float</span>(BEDARF.<span class="bu">max</span>())),</span>
<span id="cb7-91"><a href="#cb7-91" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Optimistisch: Bestfall&quot;</span>, <span class="bu">float</span>(BEDARF.<span class="bu">min</span>())),</span>
<span id="cb7-92"><a href="#cb7-92" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb7-93"><a href="#cb7-93" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, kap <span class="kw">in</span> strategien:</span>
<span id="cb7-94"><a href="#cb7-94" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> kosten_bei(kap)</span>
<span id="cb7-95"><a href="#cb7-95" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>kap<span class="sc">:&gt;11.1f}</span><span class="ss"> </span><span class="sc">{</span>kosten<span class="sc">:&gt;14,.0f}</span><span class="ss"> &quot;</span></span>
<span id="cb7-96"><a href="#cb7-96" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>kosten <span class="op">-</span> optimal<span class="sc">:&gt;+13,.0f}</span><span class="ss">&quot;</span>)</span>
<span id="cb7-97"><a href="#cb7-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-98"><a href="#cb7-98" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- EVPI: Was waere perfekte Voraussicht wert? ----------------------</span></span>
<span id="cb7-99"><a href="#cb7-99" aria-hidden="true" tabindex="-1"></a> <span class="co"># Bei perfekter Information wuerde man je Szenario genau den Bedarf kaufen.</span></span>
<span id="cb7-100"><a href="#cb7-100" aria-hidden="true" tabindex="-1"></a> kosten_perfekt <span class="op">=</span> <span class="bu">float</span>(WAHRSCHEINLICHKEIT <span class="op">@</span> (KOSTEN_VORAB <span class="op">*</span> BEDARF))</span>
<span id="cb7-101"><a href="#cb7-101" aria-hidden="true" tabindex="-1"></a> evpi <span class="op">=</span> optimal <span class="op">-</span> kosten_perfekt</span>
<span id="cb7-102"><a href="#cb7-102" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-103"><a href="#cb7-103" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Kosten bei perfekter Voraussicht: </span><span class="sc">{</span>kosten_perfekt<span class="sc">:&gt;10,.0f}</span><span class="ss"> EUR&quot;</span>)</span>
<span id="cb7-104"><a href="#cb7-104" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Wert perfekter Information (EVPI): </span><span class="sc">{</span>evpi<span class="sc">:&gt;10,.0f}</span><span class="ss"> EUR &quot;</span></span>
<span id="cb7-105"><a href="#cb7-105" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(</span><span class="sc">{</span>evpi<span class="op">/</span>optimal<span class="op">*</span><span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> % der Kosten)&quot;</span>)</span>
<span id="cb7-106"><a href="#cb7-106" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; -&gt; So viel duerfte eine perfekte Bedarfsprognose hoechstens kosten.&quot;</span>)</span>
<span id="cb7-107"><a href="#cb7-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-108"><a href="#cb7-108" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Automatische Interpretation --------------------------------------</span></span>
<span id="cb7-109"><a href="#cb7-109" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span>
<span id="cb7-110"><a href="#cb7-110" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> kapazitaet <span class="op">&gt;</span> mittelwert <span class="op">+</span> <span class="fl">1e-6</span>:</span>
<span id="cb7-111"><a href="#cb7-111" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Analyse: Der Solver waehlt </span><span class="sc">{</span>kapazitaet<span class="sc">:.0f}</span><span class="ss"> Einheiten und damit MEHR als&quot;</span>)</span>
<span id="cb7-112"><a href="#cb7-112" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;den Mittelwert (</span><span class="sc">{</span>mittelwert<span class="sc">:.0f}</span><span class="ss">), weil Unterdeckung (</span><span class="sc">{</span>KOSTEN_SPOT<span class="sc">:.0f}</span><span class="ss"> EUR)&quot;</span>)</span>
<span id="cb7-113"><a href="#cb7-113" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;deutlich teurer ist als Leerlauf (</span><span class="sc">{</span>KOSTEN_LEERLAUF<span class="sc">:.0f}</span><span class="ss"> EUR).&quot;</span>)</span>
<span id="cb7-114"><a href="#cb7-114" aria-hidden="true" tabindex="-1"></a> <span class="cf">elif</span> kapazitaet <span class="op">&lt;</span> mittelwert <span class="op">-</span> <span class="fl">1e-6</span>:</span>
<span id="cb7-115"><a href="#cb7-115" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Analyse: Der Solver waehlt </span><span class="sc">{</span>kapazitaet<span class="sc">:.0f}</span><span class="ss"> und damit WENIGER als den&quot;</span>)</span>
<span id="cb7-116"><a href="#cb7-116" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Mittelwert (</span><span class="sc">{</span>mittelwert<span class="sc">:.0f}</span><span class="ss">) - Leerlauf ist hier teurer als Zukauf.&quot;</span>)</span>
<span id="cb7-117"><a href="#cb7-117" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb7-118"><a href="#cb7-118" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Analyse: Kapazitaet entspricht dem Mittelwert (symmetrische Kosten).&quot;</span>)</span>
<span id="cb7-119"><a href="#cb7-119" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">82</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe:</strong></p>
<pre><code>==================================================================================
STOCHASTISCHE TWO-STAGE OPTIMIERUNG (KAPAZITAETSPLANUNG)
==================================================================================
Status: optimal
Erwarteter Bedarf (Mittelwert): 225.0 Einheiten
Optimale Stufe-1-Kapazitaet x*: 250.0 Einheiten
Minimale erwartete Gesamtkosten: 16,375.00 EUR
Szenario Wahrsch. Bedarf Basis genutzt Spot-Zukauf Leerlauf Kosten (EUR)
Ruhig 50 % 100.0 100.0 0.0 150.0 10,750
Volatil 30 % 250.0 250.0 0.0 0.0 10,000
Crash 20 % 500.0 250.0 250.0 0.0 40,000
----------------------------------------------------------------------------------
Vergleich verschiedener Planungsstrategien:
Strategie Kapazitaet Erw. Kosten Mehrkosten
----------------------------------------------------------------------------------
Stochastisch optimal 250.0 16,375 -0
Naiv: Mittelwert einsetzen 225.0 16,813 +437
Vorsichtig: Worst Case abdecken 500.0 21,375 +5,000
Optimistisch: Bestfall 100.0 19,000 +2,625
----------------------------------------------------------------------------------
Kosten bei perfekter Voraussicht: 9,000 EUR
Wert perfekter Information (EVPI): 7,375 EUR (45.0 % der Kosten)
-&gt; So viel duerfte eine perfekte Bedarfsprognose hoechstens kosten.
----------------------------------------------------------------------------------
Analyse: Der Solver waehlt 250 Einheiten und damit MEHR als
den Mittelwert (225), weil Unterdeckung (120 EUR)
deutlich teurer ist als Leerlauf (5 EUR).
==================================================================================</code></pre>
<p><strong>Lesen Sie die Vergleichstabelle von unten nach oben.</strong> Die beiden extremen Haltungen sind die teuersten: Wer den Worst Case abdeckt (500 Einheiten), zahlt 5 000 € zu viel für Kapazität, die in 80 % der Fälle brachliegt. Wer optimistisch plant (100), zahlt 2 625 € Strafe für ständige Notzukäufe. Die naive Mittelwertplanung liegt dazwischen — aber eben auch nicht optimal.</p>
<p>Der <strong>EVPI von 7 375 €</strong> ist bemerkenswert hoch: 45 % der Gesamtkosten entstehen allein daraus, dass man die Zukunft nicht kennt. In so einem Fall lohnt sich Investition in bessere Prognosen tatsächlich — anders als in dem Beispiel aus der Aufgabe <em>EVPI interpretieren</em> (<a href="#sec:unsicherheit-uebungsaufgaben">Abschnitt 12.8</a>).</p>
<blockquote>
<p><strong>💡 Was ist der EVPI?</strong> Der <strong>Expected Value of Perfect Information</strong> beziffert, wie viel eine perfekte Prognose wert wäre: die Differenz zwischen den Kosten unter Unsicherheit und den Kosten bei vollständigem Wissen. Er ist eine <strong>Obergrenze für jedes Prognoseprojekt</strong>. Wenn der EVPI bei 12 000 € pro Jahr liegt, lohnt sich keine Prognosesoftware für 50 000 € — selbst wenn sie perfekt wäre. Diese Zahl bewahrt Projekte vor teuren Fehlinvestitionen.</p>
</blockquote>
<hr />
<h2 id="sec:unsicherheit-robuste-optimierung-gegen-den-worst-case">12.6 Robuste Optimierung: gegen den Worst Case absichern</h2>
<p>Wenn Wahrscheinlichkeiten unbekannt oder instabil sind — Marktcrashs, Lieferkettenabrisse, Pandemien —, hilft die <strong>robuste Optimierung</strong>. Sie fragt nicht nach dem Mittel, sondern nach dem Schlimmsten:</p>
<p><span class="math display">
\min_{\mathbf{x}\in\mathcal{X}}\ \max_{\mathbf{u}\in\mathcal{U}}\ f(\mathbf{x},\mathbf{u})
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> Zwei ineinandergeschachtelte Optimierungen: * Das <strong>innere</strong> <span class="math inline">\max</span> ist der „Gegner“: Er wählt aus der Unsicherheitsmenge <span class="math inline">\mathcal{U}</span> die für Sie ungünstigsten Parameter. * Das <strong>äußere</strong> <span class="math inline">\min</span> sind Sie: Sie wählen <span class="math inline">\mathbf{x}</span> so, dass Sie selbst gegen diesen Gegner am besten dastehen.</p>
<p><strong>Ohne Formel gesagt:</strong> „Entscheide so, dass du auch dann noch gut dastehst, wenn alles gegen dich läuft.“ Das ist Spieltheorie gegen die Natur.</p>
</blockquote>
<p><strong>Der Trick, der es lösbar macht.</strong> Ein Min-Max-Problem klingt unlösbar — man müsste ja unendlich viele Parameterkombinationen prüfen. Für einfache Unsicherheitsmengen lässt sich das innere Maximum aber <strong>geschlossen ausrechnen</strong>. Beispiel Box-Unsicherheit: Die Renditen liegen in Intervallen <span class="math inline">\mu_i \in [\hat\mu_i - \delta_i,\ \hat\mu_i + \delta_i]</span>. Dann gilt für <span class="math inline">\mathbf{w} \ge 0</span>:</p>
<p><span class="math display">
\min_{\boldsymbol{\mu}\in\mathcal{U}} \boldsymbol{\mu}^\top\mathbf{w}
= \sum_i (\hat\mu_i - \delta_i)w_i
= \hat{\boldsymbol{\mu}}^\top\mathbf{w} - \boldsymbol{\delta}^\top\mathbf{w}
</span></p>
<p>Das Min-Max-Problem wird damit zu einem <strong>gewöhnlichen</strong> Optimierungsproblem mit einem Abzugsterm. Genau das macht robuste Optimierung praktikabel.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Robuste_Optimierung.py</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Unsicherheit: Robuste Portfolio-Optimierung.</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="co">Vergleicht drei Haltungen zur Unsicherheit:</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="co"> (a) nominal - vertraut den Punktschaetzungen blind</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="co"> (b) robust - sichert gegen Box-Unsicherheit ab (Worst Case)</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="co"> (c) stochastisch - optimiert den Erwartungswert ueber Szenarien</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="co">und misst den &quot;Preis der Robustheit&quot;: Wie viel Ertrag kostet die Absicherung</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="co">im Normalfall - und wie viel Verlust erspart sie im Ernstfall?</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a>ASSETS <span class="op">=</span> [<span class="st">&quot;Aktien Welt&quot;</span>, <span class="st">&quot;Anleihen&quot;</span>, <span class="st">&quot;Rohstoffe&quot;</span>, <span class="st">&quot;Immobilien&quot;</span>]</span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a>MU_SCHAETZUNG <span class="op">=</span> np.array([<span class="fl">0.085</span>, <span class="fl">0.030</span>, <span class="fl">0.055</span>, <span class="fl">0.060</span>]) <span class="co"># Punktschaetzung</span></span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a>UNSICHERHEIT <span class="op">=</span> np.array([<span class="fl">0.040</span>, <span class="fl">0.008</span>, <span class="fl">0.045</span>, <span class="fl">0.025</span>]) <span class="co"># +/- delta je Titel</span></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a>VOLA <span class="op">=</span> np.array([<span class="fl">0.17</span>, <span class="fl">0.05</span>, <span class="fl">0.22</span>, <span class="fl">0.12</span>])</span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true" tabindex="-1"></a>KORR <span class="op">=</span> np.array([</span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true" tabindex="-1"></a> [<span class="fl">1.00</span>, <span class="op">-</span><span class="fl">0.15</span>, <span class="fl">0.35</span>, <span class="fl">0.55</span>],</span>
<span id="cb9-25"><a href="#cb9-25" aria-hidden="true" tabindex="-1"></a> [<span class="op">-</span><span class="fl">0.15</span>, <span class="fl">1.00</span>, <span class="op">-</span><span class="fl">0.05</span>, <span class="fl">0.10</span>],</span>
<span id="cb9-26"><a href="#cb9-26" aria-hidden="true" tabindex="-1"></a> [<span class="fl">0.35</span>, <span class="op">-</span><span class="fl">0.05</span>, <span class="fl">1.00</span>, <span class="fl">0.25</span>],</span>
<span id="cb9-27"><a href="#cb9-27" aria-hidden="true" tabindex="-1"></a> [<span class="fl">0.55</span>, <span class="fl">0.10</span>, <span class="fl">0.25</span>, <span class="fl">1.00</span>],</span>
<span id="cb9-28"><a href="#cb9-28" aria-hidden="true" tabindex="-1"></a>])</span>
<span id="cb9-29"><a href="#cb9-29" aria-hidden="true" tabindex="-1"></a>SIGMA <span class="op">=</span> np.diag(VOLA) <span class="op">@</span> KORR <span class="op">@</span> np.diag(VOLA)</span>
<span id="cb9-30"><a href="#cb9-30" aria-hidden="true" tabindex="-1"></a>LAMBDA <span class="op">=</span> <span class="fl">4.0</span> <span class="co"># Risikoaversion</span></span>
<span id="cb9-31"><a href="#cb9-31" aria-hidden="true" tabindex="-1"></a>N <span class="op">=</span> <span class="bu">len</span>(ASSETS)</span>
<span id="cb9-32"><a href="#cb9-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-33"><a href="#cb9-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-34"><a href="#cb9-34" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> optimiere(mu_effektiv):</span>
<span id="cb9-35"><a href="#cb9-35" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Standard-Mean-Variance mit vorgegebenem Renditevektor.&quot;&quot;&quot;</span></span>
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true" tabindex="-1"></a> w <span class="op">=</span> cp.Variable(N, nonneg<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true" tabindex="-1"></a> ziel <span class="op">=</span> cp.Maximize(mu_effektiv <span class="op">@</span> w <span class="op">-</span> <span class="fl">0.5</span> <span class="op">*</span> LAMBDA <span class="op">*</span> cp.quad_form(w, SIGMA))</span>
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(ziel, [cp.<span class="bu">sum</span>(w) <span class="op">==</span> <span class="dv">1</span>])</span>
<span id="cb9-39"><a href="#cb9-39" aria-hidden="true" tabindex="-1"></a> problem.solve()</span>
<span id="cb9-40"><a href="#cb9-40" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> w.value</span>
<span id="cb9-41"><a href="#cb9-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-42"><a href="#cb9-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-43"><a href="#cb9-43" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kennzahlen(w, mu):</span>
<span id="cb9-44"><a href="#cb9-44" aria-hidden="true" tabindex="-1"></a> ertrag <span class="op">=</span> <span class="bu">float</span>(mu <span class="op">@</span> w)</span>
<span id="cb9-45"><a href="#cb9-45" aria-hidden="true" tabindex="-1"></a> risiko <span class="op">=</span> <span class="bu">float</span>(np.sqrt(w <span class="op">@</span> SIGMA <span class="op">@</span> w))</span>
<span id="cb9-46"><a href="#cb9-46" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ertrag, risiko</span>
<span id="cb9-47"><a href="#cb9-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-48"><a href="#cb9-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-49"><a href="#cb9-49" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb9-50"><a href="#cb9-50" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-51"><a href="#cb9-51" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; ROBUSTE vs. NOMINALE PORTFOLIO-OPTIMIERUNG&quot;</span>)</span>
<span id="cb9-52"><a href="#cb9-52" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-53"><a href="#cb9-53" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Asset&#39;</span><span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Erw. Rendite&#39;</span><span class="sc">:&gt;14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Unsicherheit&#39;</span><span class="sc">:&gt;14}</span><span class="ss"> &quot;</span></span>
<span id="cb9-54"><a href="#cb9-54" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Worst Case&#39;</span><span class="sc">:&gt;12}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Volatilitaet&#39;</span><span class="sc">:&gt;13}</span><span class="ss">&quot;</span>)</span>
<span id="cb9-55"><a href="#cb9-55" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-56"><a href="#cb9-56" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i, name <span class="kw">in</span> <span class="bu">enumerate</span>(ASSETS):</span>
<span id="cb9-57"><a href="#cb9-57" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span>MU_SCHAETZUNG[i]<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;13.1f}</span><span class="ss"> % &quot;</span></span>
<span id="cb9-58"><a href="#cb9-58" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;+/- &#39;</span> <span class="op">+</span> <span class="bu">format</span>(UNSICHERHEIT[i]<span class="op">*</span><span class="dv">100</span>, <span class="st">&#39;.1f&#39;</span>) <span class="op">+</span> <span class="st">&#39; %&#39;</span><span class="sc">:&gt;14}</span><span class="ss"> &quot;</span></span>
<span id="cb9-59"><a href="#cb9-59" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>(MU_SCHAETZUNG[i]<span class="op">-</span>UNSICHERHEIT[i])<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;11.1f}</span><span class="ss"> % &quot;</span></span>
<span id="cb9-60"><a href="#cb9-60" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>VOLA[i]<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;12.1f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb9-61"><a href="#cb9-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-62"><a href="#cb9-62" aria-hidden="true" tabindex="-1"></a> <span class="co"># (a) nominal: vertraut den Schaetzungen</span></span>
<span id="cb9-63"><a href="#cb9-63" aria-hidden="true" tabindex="-1"></a> w_nominal <span class="op">=</span> optimiere(MU_SCHAETZUNG)</span>
<span id="cb9-64"><a href="#cb9-64" aria-hidden="true" tabindex="-1"></a> <span class="co"># (b) robust: rechnet mit dem Worst Case der Box-Unsicherheitsmenge</span></span>
<span id="cb9-65"><a href="#cb9-65" aria-hidden="true" tabindex="-1"></a> w_robust <span class="op">=</span> optimiere(MU_SCHAETZUNG <span class="op">-</span> UNSICHERHEIT)</span>
<span id="cb9-66"><a href="#cb9-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-67"><a href="#cb9-67" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-68"><a href="#cb9-68" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;nominal&#39;</span><span class="sc">:&gt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;robust&#39;</span><span class="sc">:&gt;22}</span><span class="ss">&quot;</span>)</span>
<span id="cb9-69"><a href="#cb9-69" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-70"><a href="#cb9-70" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i, name <span class="kw">in</span> <span class="bu">enumerate</span>(ASSETS):</span>
<span id="cb9-71"><a href="#cb9-71" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span>w_nominal[i]<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.1f}</span><span class="ss"> % </span><span class="sc">{</span>w_robust[i]<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.1f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb9-72"><a href="#cb9-72" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-73"><a href="#cb9-73" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Bewertung in beiden Welten --------------------------------------</span></span>
<span id="cb9-74"><a href="#cb9-74" aria-hidden="true" tabindex="-1"></a> mu_worst <span class="op">=</span> MU_SCHAETZUNG <span class="op">-</span> UNSICHERHEIT</span>
<span id="cb9-75"><a href="#cb9-75" aria-hidden="true" tabindex="-1"></a> e_nom_gut, r_nom <span class="op">=</span> kennzahlen(w_nominal, MU_SCHAETZUNG)</span>
<span id="cb9-76"><a href="#cb9-76" aria-hidden="true" tabindex="-1"></a> e_rob_gut, r_rob <span class="op">=</span> kennzahlen(w_robust, MU_SCHAETZUNG)</span>
<span id="cb9-77"><a href="#cb9-77" aria-hidden="true" tabindex="-1"></a> e_nom_schlecht, _ <span class="op">=</span> kennzahlen(w_nominal, mu_worst)</span>
<span id="cb9-78"><a href="#cb9-78" aria-hidden="true" tabindex="-1"></a> e_rob_schlecht, _ <span class="op">=</span> kennzahlen(w_robust, mu_worst)</span>
<span id="cb9-79"><a href="#cb9-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-80"><a href="#cb9-80" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-81"><a href="#cb9-81" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Bewertung&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;nominales Portfolio&#39;</span><span class="sc">:&gt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;robustes Portfolio&#39;</span><span class="sc">:&gt;22}</span><span class="ss">&quot;</span>)</span>
<span id="cb9-82"><a href="#cb9-82" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-83"><a href="#cb9-83" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Ertrag, wenn Schaetzung stimmt&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>e_nom_gut<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> % &quot;</span></span>
<span id="cb9-84"><a href="#cb9-84" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>e_rob_gut<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb9-85"><a href="#cb9-85" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Ertrag im Worst Case&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>e_nom_schlecht<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> % &quot;</span></span>
<span id="cb9-86"><a href="#cb9-86" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>e_rob_schlecht<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb9-87"><a href="#cb9-87" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Volatilitaet&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>r_nom<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> % </span><span class="sc">{</span>r_rob<span class="op">*</span><span class="dv">100</span><span class="sc">:&gt;21.2f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb9-88"><a href="#cb9-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-89"><a href="#cb9-89" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span>
<span id="cb9-90"><a href="#cb9-90" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Preis der Robustheit (Ertragsverzicht im Normalfall): &quot;</span></span>
<span id="cb9-91"><a href="#cb9-91" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>(e_nom_gut <span class="op">-</span> e_rob_gut)<span class="op">*</span><span class="dv">100</span><span class="sc">:+.2f}</span><span class="ss"> Prozentpunkte&quot;</span>)</span>
<span id="cb9-92"><a href="#cb9-92" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Nutzen der Robustheit (Vorteil im Worst Case): &quot;</span></span>
<span id="cb9-93"><a href="#cb9-93" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>(e_rob_schlecht <span class="op">-</span> e_nom_schlecht)<span class="op">*</span><span class="dv">100</span><span class="sc">:+.2f}</span><span class="ss"> Prozentpunkte&quot;</span>)</span>
<span id="cb9-94"><a href="#cb9-94" aria-hidden="true" tabindex="-1"></a> verhaeltnis <span class="op">=</span> ((e_rob_schlecht <span class="op">-</span> e_nom_schlecht)</span>
<span id="cb9-95"><a href="#cb9-95" aria-hidden="true" tabindex="-1"></a> <span class="op">/</span> <span class="bu">max</span>(e_nom_gut <span class="op">-</span> e_rob_gut, <span class="fl">1e-9</span>))</span>
<span id="cb9-96"><a href="#cb9-96" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Verhaeltnis Nutzen/Preis: </span><span class="sc">{</span>verhaeltnis<span class="sc">:.2f}</span><span class="ss">&quot;</span>)</span>
<span id="cb9-97"><a href="#cb9-97" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; -&gt; Werte &gt; 1 bedeuten: Die Absicherung bringt im Ernstfall mehr,&quot;</span>)</span>
<span id="cb9-98"><a href="#cb9-98" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; als sie im Normalfall kostet.&quot;</span>)</span>
<span id="cb9-99"><a href="#cb9-99" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">88</span>)</span></code></pre></div>
<blockquote>
<p><strong>⚠️ Der Preis der Robustheit</strong> Robuste Modelle sind <strong>konservativ</strong>. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. <strong>Unsicherheitsmenge realistisch wählen.</strong> Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. <strong>Budgeted Uncertainty (Bertsimas/Sim)</strong>: Man nimmt an, dass höchstens <span class="math inline">\Gamma</span> von <span class="math inline">n</span> Parametern gleichzeitig ihren Worst Case annehmen. <span class="math inline">\Gamma</span> steuert die Vorsicht stufenlos. 3. <strong>Nutzen und Preis immer beziffern</strong> — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.</p>
</blockquote>
<hr />
<h2 id="sec:unsicherheit-chance-constraints">12.7 Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“</h2>
<p>Die drei bisherigen Ansätze beantworten Fragen, die im Sitzungssaal selten so gestellt werden. „Was ist im Mittel am besten?“ hört ein Vorstand als Ausflucht; „was hält auch im schlimmsten Fall?“ als Einladung, zu viel Geld auszugeben. Gefragt wird fast immer nach einer <strong>Wahrscheinlichkeit</strong>: <em>Mit welcher Sicherheit hält der Plan?</em></p>
<p>Die <strong>Wahrscheinlichkeitsbeschränkung</strong> schreibt genau das ins Modell:</p>
<p><span class="math display">
\mathbb{P}\!\left(\mathbf{a}^\top\mathbf{x} \ge b\right) \ge 1 - \alpha
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <span class="math inline">\mathbf{a}</span> — der <strong>unsichere</strong> Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist keine Zahl, sondern eine Zufallsgröße. * <span class="math inline">\mathbf{x}</span> — Ihre Entscheidung. Sie fällt, <strong>bevor</strong> <span class="math inline">\mathbf{a}</span> sich zeigt. * <span class="math inline">\alpha</span> — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. <span class="math inline">\alpha = 0{,}05</span> heißt „in höchstens 5 % der Fälle darf es schiefgehen“.</p>
<p><strong>Ohne Formel gesagt:</strong> „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“</p>
</blockquote>
<p><strong>Das Problem daran.</strong> So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.</p>
<h3 id="weg-1-normalverteilung-die-bedingung-wird-ein-kegel">Weg 1: Normalverteilung — die Bedingung wird ein Kegel</h3>
<p>Ist <span class="math inline">\mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma})</span>, dann ist <span class="math inline">\mathbf{a}^\top\mathbf{x}</span> für festes <span class="math inline">\mathbf{x}</span> selbst normalverteilt, mit Erwartungswert <span class="math inline">\hat{\mathbf{a}}^\top\mathbf{x}</span> und Standardabweichung <span class="math inline">\sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}</span>. Damit lässt sich die Wahrscheinlichkeit durch ein Quantil ersetzen:</p>
<p><span class="math display">
\hat{\mathbf{a}}^\top\mathbf{x} \;-\; z_{1-\alpha}\,\lVert \mathbf{L}^\top\mathbf{x}\rVert_2
\;\ge\; b
\qquad\text{mit}\qquad
\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad
z_{1-\alpha} = \Phi^{-1}(1-\alpha)
</span></p>
<p>Das ist eine <strong>Second-Order-Cone-Bedingung</strong> — dieselbe Bauform, die <a href="qp-nlp.html#kap-qp-nlp">Kapitel 11</a> eingeführt hat, und CVXPY löst sie ohne Umstände.</p>
<blockquote>
<p><strong>🎯 Warum eine Norm und keine Summe</strong> Der Sicherheitszuschlag <span class="math inline">\lVert\mathbf{L}^\top\mathbf{x}\rVert_2</span> ist <strong>keine</strong> Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken <strong>gemeinsam</strong> 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.</p>
</blockquote>
<h3 id="weg-2-szenarien-big-m-ohne-verteilungsannahme">Weg 2: Szenarien — Big-M ohne Verteilungsannahme</h3>
<p>Liegen statt einer Verteilung nur <span class="math inline">S</span> beobachtete Szenarien vor, bekommt jedes eine Binärvariable <span class="math inline">z_s</span>, die sagt, ob es verletzt werden <strong>darf</strong>:</p>
<p><span class="math display">
\begin{aligned}
\mathbf{a}_s^\top\mathbf{x} &amp;\ge b - M z_s &amp;&amp; \forall s\\
\sum_{s} z_s &amp;\le \alpha S\\
z_s &amp;\in \{0,1\}
\end{aligned}
</span></p>
<p>Aus dem Kegelproblem wird ein MILP mit <span class="math inline">S</span> Binärvariablen. Für <span class="math inline">M</span> gilt, was <a href="milp.html#kap-milp">Kapitel 6</a> über die Big-M-Falle sagt: <strong>so klein wie möglich</strong>. Hier ist <span class="math inline">M = b</span> die kleinste gültige Schranke, denn <span class="math inline">\mathbf{a}_s^\top\mathbf{x} \ge 0</span> — größer kann eine Verletzung gar nicht ausfallen.</p>
<h3 id="der-fall-ein-kraftwerkspark-mit-500-mw-zusage">Der Fall: ein Kraftwerkspark mit 500 MW Zusage</h3>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Chance_Constraints.py</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen.</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="co">Das Management fragt selten &quot;was ist im Mittel am besten?&quot;, sondern &quot;mit</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="co">welcher Sicherheit haelt der Plan?&quot;. Genau das formuliert eine Chance</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="co">Constraint: P(Versorgung &gt;= Bedarf) &gt;= 1 - alpha.</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="co">Gezeigt werden beide Wege dorthin, an derselben Instanz:</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="co"> (a) analytisch - unter Normalverteilungsannahme wird daraus eine</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a><span class="co"> Second-Order-Cone-Bedingung, loesbar mit CVXPY</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a><span class="co"> (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a><span class="co"> empirische Verteilungen</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a><span class="co">Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a><span class="co">Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a><span class="co">nicht normal ist?</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a><span class="co">Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools</span></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a><span class="co">noch ein direkter highspy-Import, damit alles in einem Prozess laeuft.</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> cvxpy <span class="im">as</span> cp</span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.stats <span class="im">import</span> norm</span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true" tabindex="-1"></a><span class="co"># --- Der Kraftwerkspark ----------------------------------------------------</span></span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true" tabindex="-1"></a><span class="co"># Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel</span></span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true" tabindex="-1"></a><span class="co"># wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und</span></span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true" tabindex="-1"></a><span class="co"># Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche,</span></span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true" tabindex="-1"></a><span class="co"># Genehmigung, Brennstoffversorgung.</span></span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true" tabindex="-1"></a>TECHNIK <span class="op">=</span> [<span class="st">&quot;Gaskraftwerk&quot;</span>, <span class="st">&quot;Windpark&quot;</span>, <span class="st">&quot;Solarpark&quot;</span>, <span class="st">&quot;Biomasse&quot;</span>]</span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true" tabindex="-1"></a>KOSTEN <span class="op">=</span> np.array([<span class="fl">65_000.0</span>, <span class="fl">24_000.0</span>, <span class="fl">12_000.0</span>, <span class="fl">60_000.0</span>]) <span class="co"># EUR je MW und Jahr</span></span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true" tabindex="-1"></a>VERFUEGBAR <span class="op">=</span> np.array([<span class="fl">0.92</span>, <span class="fl">0.35</span>, <span class="fl">0.18</span>, <span class="fl">0.85</span>])</span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true" tabindex="-1"></a>STREUUNG <span class="op">=</span> np.array([<span class="fl">0.05</span>, <span class="fl">0.16</span>, <span class="fl">0.10</span>, <span class="fl">0.04</span>])</span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true" tabindex="-1"></a>GRENZE <span class="op">=</span> np.array([<span class="fl">400.0</span>, <span class="fl">250.0</span>, <span class="fl">600.0</span>, <span class="fl">350.0</span>]) <span class="co"># MW</span></span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a><span class="co"># Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und</span></span>
<span id="cb10-41"><a href="#cb10-41" aria-hidden="true" tabindex="-1"></a><span class="co"># Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel</span></span>
<span id="cb10-42"><a href="#cb10-42" aria-hidden="true" tabindex="-1"></a><span class="co"># und nicht zu einer Summe unabhaengiger Einzelzuschlaege.</span></span>
<span id="cb10-43"><a href="#cb10-43" aria-hidden="true" tabindex="-1"></a>KORRELATION <span class="op">=</span> np.array([</span>
<span id="cb10-44"><a href="#cb10-44" aria-hidden="true" tabindex="-1"></a> [<span class="fl">1.00</span>, <span class="fl">0.00</span>, <span class="fl">0.00</span>, <span class="fl">0.05</span>],</span>
<span id="cb10-45"><a href="#cb10-45" aria-hidden="true" tabindex="-1"></a> [<span class="fl">0.00</span>, <span class="fl">1.00</span>, <span class="op">-</span><span class="fl">0.25</span>, <span class="fl">0.00</span>],</span>
<span id="cb10-46"><a href="#cb10-46" aria-hidden="true" tabindex="-1"></a> [<span class="fl">0.00</span>, <span class="op">-</span><span class="fl">0.25</span>, <span class="fl">1.00</span>, <span class="fl">0.00</span>],</span>
<span id="cb10-47"><a href="#cb10-47" aria-hidden="true" tabindex="-1"></a> [<span class="fl">0.05</span>, <span class="fl">0.00</span>, <span class="fl">0.00</span>, <span class="fl">1.00</span>],</span>
<span id="cb10-48"><a href="#cb10-48" aria-hidden="true" tabindex="-1"></a>])</span>
<span id="cb10-49"><a href="#cb10-49" aria-hidden="true" tabindex="-1"></a>SIGMA <span class="op">=</span> np.diag(STREUUNG) <span class="op">@</span> KORRELATION <span class="op">@</span> np.diag(STREUUNG)</span>
<span id="cb10-50"><a href="#cb10-50" aria-hidden="true" tabindex="-1"></a>WURZEL <span class="op">=</span> np.linalg.cholesky(SIGMA) <span class="co"># L mit L @ L.T == SIGMA</span></span>
<span id="cb10-51"><a href="#cb10-51" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-52"><a href="#cb10-52" aria-hidden="true" tabindex="-1"></a>BEDARF <span class="op">=</span> <span class="fl">500.0</span> <span class="co"># MW, die gesichert bereitstehen muessen</span></span>
<span id="cb10-53"><a href="#cb10-53" aria-hidden="true" tabindex="-1"></a>N <span class="op">=</span> <span class="bu">len</span>(TECHNIK)</span>
<span id="cb10-54"><a href="#cb10-54" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-55"><a href="#cb10-55" aria-hidden="true" tabindex="-1"></a><span class="co"># Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich.</span></span>
<span id="cb10-56"><a href="#cb10-56" aria-hidden="true" tabindex="-1"></a><span class="co"># Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das</span></span>
<span id="cb10-57"><a href="#cb10-57" aria-hidden="true" tabindex="-1"></a><span class="co"># Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt.</span></span>
<span id="cb10-58"><a href="#cb10-58" aria-hidden="true" tabindex="-1"></a><span class="co"># Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken.</span></span>
<span id="cb10-59"><a href="#cb10-59" aria-hidden="true" tabindex="-1"></a>P_KAELTEWELLE <span class="op">=</span> <span class="fl">0.08</span></span>
<span id="cb10-60"><a href="#cb10-60" aria-hidden="true" tabindex="-1"></a>EINBRUCH <span class="op">=</span> np.array([<span class="fl">0.72</span>, <span class="fl">0.10</span>, <span class="fl">0.15</span>, <span class="fl">0.88</span>])</span>
<span id="cb10-61"><a href="#cb10-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-62"><a href="#cb10-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-63"><a href="#cb10-63" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> mittelwertplan():</span>
<span id="cb10-64"><a href="#cb10-64" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.&quot;&quot;&quot;</span></span>
<span id="cb10-65"><a href="#cb10-65" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable(N, nonneg<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb10-66"><a href="#cb10-66" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Minimize(KOSTEN <span class="op">@</span> x),</span>
<span id="cb10-67"><a href="#cb10-67" aria-hidden="true" tabindex="-1"></a> [VERFUEGBAR <span class="op">@</span> x <span class="op">&gt;=</span> BEDARF, x <span class="op">&lt;=</span> GRENZE])</span>
<span id="cb10-68"><a href="#cb10-68" aria-hidden="true" tabindex="-1"></a> problem.solve(solver<span class="op">=</span>cp.CLARABEL)</span>
<span id="cb10-69"><a href="#cb10-69" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> problem.value, x.value</span>
<span id="cb10-70"><a href="#cb10-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-71"><a href="#cb10-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-72"><a href="#cb10-72" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> chance_constraint_analytisch(alpha):</span>
<span id="cb10-73"><a href="#cb10-73" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;</span></span>
<span id="cb10-74"><a href="#cb10-74" aria-hidden="true" tabindex="-1"></a><span class="co"> P(a^T x &gt;= BEDARF) &gt;= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA).</span></span>
<span id="cb10-75"><a href="#cb10-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-76"><a href="#cb10-76" aria-hidden="true" tabindex="-1"></a><span class="co"> Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 &gt;= BEDARF</span></span>
<span id="cb10-77"><a href="#cb10-77" aria-hidden="true" tabindex="-1"></a><span class="co"> mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der</span></span>
<span id="cb10-78"><a href="#cb10-78" aria-hidden="true" tabindex="-1"></a><span class="co"> Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage.</span></span>
<span id="cb10-79"><a href="#cb10-79" aria-hidden="true" tabindex="-1"></a><span class="co"> Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken</span></span>
<span id="cb10-80"><a href="#cb10-80" aria-hidden="true" tabindex="-1"></a><span class="co"> gemeinsam weniger als jede fuer sich.</span></span>
<span id="cb10-81"><a href="#cb10-81" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb10-82"><a href="#cb10-82" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable(N, nonneg<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb10-83"><a href="#cb10-83" aria-hidden="true" tabindex="-1"></a> z <span class="op">=</span> norm.ppf(<span class="dv">1</span> <span class="op">-</span> alpha)</span>
<span id="cb10-84"><a href="#cb10-84" aria-hidden="true" tabindex="-1"></a> bedingung <span class="op">=</span> VERFUEGBAR <span class="op">@</span> x <span class="op">-</span> z <span class="op">*</span> cp.norm(WURZEL.T <span class="op">@</span> x, <span class="dv">2</span>) <span class="op">&gt;=</span> BEDARF</span>
<span id="cb10-85"><a href="#cb10-85" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(cp.Minimize(KOSTEN <span class="op">@</span> x), [bedingung, x <span class="op">&lt;=</span> GRENZE])</span>
<span id="cb10-86"><a href="#cb10-86" aria-hidden="true" tabindex="-1"></a> problem.solve(solver<span class="op">=</span>cp.CLARABEL)</span>
<span id="cb10-87"><a href="#cb10-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> problem.value, x.value</span>
<span id="cb10-88"><a href="#cb10-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-89"><a href="#cb10-89" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-90"><a href="#cb10-90" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> chance_constraint_szenarien(a, alpha):</span>
<span id="cb10-91"><a href="#cb10-91" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;</span></span>
<span id="cb10-92"><a href="#cb10-92" aria-hidden="true" tabindex="-1"></a><span class="co"> Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine</span></span>
<span id="cb10-93"><a href="#cb10-93" aria-hidden="true" tabindex="-1"></a><span class="co"> Binaervariable z_s, ob es verletzt werden darf.</span></span>
<span id="cb10-94"><a href="#cb10-94" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-95"><a href="#cb10-95" aria-hidden="true" tabindex="-1"></a><span class="co"> a_s^T x &gt;= BEDARF - M * z_s fuer alle s</span></span>
<span id="cb10-96"><a href="#cb10-96" aria-hidden="true" tabindex="-1"></a><span class="co"> sum_s z_s &lt;= alpha * S</span></span>
<span id="cb10-97"><a href="#cb10-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-98"><a href="#cb10-98" aria-hidden="true" tabindex="-1"></a><span class="co"> M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x &gt;= 0:</span></span>
<span id="cb10-99"><a href="#cb10-99" aria-hidden="true" tabindex="-1"></a><span class="co"> Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M</span></span>
<span id="cb10-100"><a href="#cb10-100" aria-hidden="true" tabindex="-1"></a><span class="co"> wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die</span></span>
<span id="cb10-101"><a href="#cb10-101" aria-hidden="true" tabindex="-1"></a><span class="co"> Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung.</span></span>
<span id="cb10-102"><a href="#cb10-102" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb10-103"><a href="#cb10-103" aria-hidden="true" tabindex="-1"></a> S <span class="op">=</span> <span class="bu">len</span>(a)</span>
<span id="cb10-104"><a href="#cb10-104" aria-hidden="true" tabindex="-1"></a> x <span class="op">=</span> cp.Variable(N, nonneg<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb10-105"><a href="#cb10-105" aria-hidden="true" tabindex="-1"></a> z <span class="op">=</span> cp.Variable(S, boolean<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb10-106"><a href="#cb10-106" aria-hidden="true" tabindex="-1"></a> problem <span class="op">=</span> cp.Problem(</span>
<span id="cb10-107"><a href="#cb10-107" aria-hidden="true" tabindex="-1"></a> cp.Minimize(KOSTEN <span class="op">@</span> x),</span>
<span id="cb10-108"><a href="#cb10-108" aria-hidden="true" tabindex="-1"></a> [a <span class="op">@</span> x <span class="op">&gt;=</span> BEDARF <span class="op">-</span> BEDARF <span class="op">*</span> z, cp.<span class="bu">sum</span>(z) <span class="op">&lt;=</span> alpha <span class="op">*</span> S, x <span class="op">&lt;=</span> GRENZE])</span>
<span id="cb10-109"><a href="#cb10-109" aria-hidden="true" tabindex="-1"></a> problem.solve(solver<span class="op">=</span>cp.SCIPY)</span>
<span id="cb10-110"><a href="#cb10-110" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> problem.value, x.value</span>
<span id="cb10-111"><a href="#cb10-111" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-112"><a href="#cb10-112" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-113"><a href="#cb10-113" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> ziehe_wetter(anzahl, seed):</span>
<span id="cb10-114"><a href="#cb10-114" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.&quot;&quot;&quot;</span></span>
<span id="cb10-115"><a href="#cb10-115" aria-hidden="true" tabindex="-1"></a> rng <span class="op">=</span> np.random.default_rng(seed)</span>
<span id="cb10-116"><a href="#cb10-116" aria-hidden="true" tabindex="-1"></a> a <span class="op">=</span> rng.multivariate_normal(VERFUEGBAR, SIGMA, size<span class="op">=</span>anzahl)</span>
<span id="cb10-117"><a href="#cb10-117" aria-hidden="true" tabindex="-1"></a> getroffen <span class="op">=</span> rng.random(anzahl) <span class="op">&lt;</span> P_KAELTEWELLE</span>
<span id="cb10-118"><a href="#cb10-118" aria-hidden="true" tabindex="-1"></a> a[getroffen] <span class="op">*=</span> EINBRUCH</span>
<span id="cb10-119"><a href="#cb10-119" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> np.clip(a, <span class="fl">0.0</span>, <span class="fl">1.0</span>)</span>
<span id="cb10-120"><a href="#cb10-120" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-121"><a href="#cb10-121" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-122"><a href="#cb10-122" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> sicherheit(x, a):</span>
<span id="cb10-123"><a href="#cb10-123" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Anteil der Szenarien, in denen der Plan den Bedarf deckt.&quot;&quot;&quot;</span></span>
<span id="cb10-124"><a href="#cb10-124" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(np.mean(a <span class="op">@</span> x <span class="op">&gt;=</span> BEDARF))</span>
<span id="cb10-125"><a href="#cb10-125" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-126"><a href="#cb10-126" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-127"><a href="#cb10-127" aria-hidden="true" tabindex="-1"></a>KOPF <span class="op">=</span> (<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Plan&#39;</span><span class="sc">:&lt;20}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Kosten (EUR)&#39;</span><span class="sc">:&gt;13}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Gas&#39;</span><span class="sc">:&gt;4}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Wind&#39;</span><span class="sc">:&gt;4}</span><span class="ss"> &quot;</span></span>
<span id="cb10-128"><a href="#cb10-128" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Sol&#39;</span><span class="sc">:&gt;4}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Bio&#39;</span><span class="sc">:&gt;4}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Normalwelt&#39;</span><span class="sc">:&gt;11}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;echte Welt&#39;</span><span class="sc">:&gt;11}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-129"><a href="#cb10-129" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-130"><a href="#cb10-130" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-131"><a href="#cb10-131" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> zeile(name, kosten, x, in_normal, in_echt):</span>
<span id="cb10-132"><a href="#cb10-132" aria-hidden="true" tabindex="-1"></a> mix <span class="op">=</span> <span class="st">&quot; &quot;</span>.join(<span class="ss">f&quot;</span><span class="sc">{</span>w<span class="sc">:4.0f}</span><span class="ss">&quot;</span> <span class="cf">for</span> w <span class="kw">in</span> x)</span>
<span id="cb10-133"><a href="#cb10-133" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;20}</span><span class="ss"> </span><span class="sc">{</span>kosten<span class="sc">:&gt;13,.0f}</span><span class="ss"> </span><span class="sc">{</span>mix<span class="sc">}</span><span class="ss"> &quot;</span></span>
<span id="cb10-134"><a href="#cb10-134" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>in_normal <span class="op">*</span> <span class="dv">100</span><span class="sc">:&gt;9.2f}</span><span class="ss"> % </span><span class="sc">{</span>in_echt <span class="op">*</span> <span class="dv">100</span><span class="sc">:&gt;9.2f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb10-135"><a href="#cb10-135" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-136"><a href="#cb10-136" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-137"><a href="#cb10-137" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb10-138"><a href="#cb10-138" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-139"><a href="#cb10-139" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK&quot;</span>)</span>
<span id="cb10-140"><a href="#cb10-140" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-141"><a href="#cb10-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Gesichert bereitzustellen: </span><span class="sc">{</span>BEDARF<span class="sc">:.0f}</span><span class="ss"> MW</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb10-142"><a href="#cb10-142" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Technologie&#39;</span><span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;EUR/MW&#39;</span><span class="sc">:&gt;8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;verfuegbar&#39;</span><span class="sc">:&gt;11}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Streuung&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb10-143"><a href="#cb10-143" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Grenze&#39;</span><span class="sc">:&gt;8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;EUR je erw. MW&#39;</span><span class="sc">:&gt;15}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-144"><a href="#cb10-144" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-145"><a href="#cb10-145" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i, name <span class="kw">in</span> <span class="bu">enumerate</span>(TECHNIK):</span>
<span id="cb10-146"><a href="#cb10-146" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;14}</span><span class="ss"> </span><span class="sc">{</span>KOSTEN[i]<span class="sc">:&gt;8,.0f}</span><span class="ss"> </span><span class="sc">{</span>VERFUEGBAR[i]<span class="sc">:&gt;10.2f}</span><span class="ss"> &quot;</span></span>
<span id="cb10-147"><a href="#cb10-147" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>STREUUNG[i]<span class="sc">:&gt;8.2f}</span><span class="ss"> </span><span class="sc">{</span>GRENZE[i]<span class="sc">:&gt;7.0f}</span><span class="ss"> &quot;</span></span>
<span id="cb10-148"><a href="#cb10-148" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>KOSTEN[i] <span class="op">/</span> VERFUEGBAR[i]<span class="sc">:&gt;15,.0f}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-149"><a href="#cb10-149" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Vollausbau liefert im Mittel </span><span class="sc">{</span>VERFUEGBAR <span class="op">@</span> GRENZE<span class="sc">:.0f}</span><span class="ss"> MW, &quot;</span></span>
<span id="cb10-150"><a href="#cb10-150" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;in der Kaeltewelle </span><span class="sc">{</span>(VERFUEGBAR <span class="op">*</span> EINBRUCH) <span class="op">@</span> GRENZE<span class="sc">:.0f}</span><span class="ss"> MW.&quot;</span>)</span>
<span id="cb10-151"><a href="#cb10-151" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-152"><a href="#cb10-152" aria-hidden="true" tabindex="-1"></a> <span class="co"># Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen -</span></span>
<span id="cb10-153"><a href="#cb10-153" aria-hidden="true" tabindex="-1"></a> <span class="co"># einmal in der unterstellten Normalwelt, einmal in der echten Verteilung.</span></span>
<span id="cb10-154"><a href="#cb10-154" aria-hidden="true" tabindex="-1"></a> echt <span class="op">=</span> ziehe_wetter(<span class="dv">200_000</span>, seed<span class="op">=</span><span class="dv">771</span>)</span>
<span id="cb10-155"><a href="#cb10-155" aria-hidden="true" tabindex="-1"></a> normalwelt <span class="op">=</span> np.random.default_rng(<span class="dv">4711</span>).multivariate_normal(</span>
<span id="cb10-156"><a href="#cb10-156" aria-hidden="true" tabindex="-1"></a> VERFUEGBAR, SIGMA, size<span class="op">=</span><span class="dv">200_000</span>)</span>
<span id="cb10-157"><a href="#cb10-157" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-158"><a href="#cb10-158" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-159"><a href="#cb10-159" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; (1) Mittelwertplan und Chance Constraints im Vergleich&quot;</span>)</span>
<span id="cb10-160"><a href="#cb10-160" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-161"><a href="#cb10-161" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(KOPF)</span>
<span id="cb10-162"><a href="#cb10-162" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-163"><a href="#cb10-163" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-164"><a href="#cb10-164" aria-hidden="true" tabindex="-1"></a> k_mittel, x_mittel <span class="op">=</span> mittelwertplan()</span>
<span id="cb10-165"><a href="#cb10-165" aria-hidden="true" tabindex="-1"></a> stufen <span class="op">=</span> [(<span class="st">&quot;Mittelwert&quot;</span>, k_mittel, sicherheit(x_mittel, normalwelt))]</span>
<span id="cb10-166"><a href="#cb10-166" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(zeile(<span class="st">&quot;Mittelwert&quot;</span>, k_mittel, x_mittel,</span>
<span id="cb10-167"><a href="#cb10-167" aria-hidden="true" tabindex="-1"></a> sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt)))</span>
<span id="cb10-168"><a href="#cb10-168" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-169"><a href="#cb10-169" aria-hidden="true" tabindex="-1"></a> plaene <span class="op">=</span> {}</span>
<span id="cb10-170"><a href="#cb10-170" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> alpha <span class="kw">in</span> (<span class="fl">0.20</span>, <span class="fl">0.10</span>, <span class="fl">0.05</span>, <span class="fl">0.01</span>):</span>
<span id="cb10-171"><a href="#cb10-171" aria-hidden="true" tabindex="-1"></a> kosten, x <span class="op">=</span> chance_constraint_analytisch(alpha)</span>
<span id="cb10-172"><a href="#cb10-172" aria-hidden="true" tabindex="-1"></a> plaene[alpha] <span class="op">=</span> (kosten, x)</span>
<span id="cb10-173"><a href="#cb10-173" aria-hidden="true" tabindex="-1"></a> quote_normal <span class="op">=</span> sicherheit(x, normalwelt)</span>
<span id="cb10-174"><a href="#cb10-174" aria-hidden="true" tabindex="-1"></a> stufen.append((<span class="ss">f&quot;Zusage </span><span class="sc">{</span>(<span class="dv">1</span> <span class="op">-</span> alpha) <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %&quot;</span>, kosten, quote_normal))</span>
<span id="cb10-175"><a href="#cb10-175" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(zeile(<span class="ss">f&quot;Zusage </span><span class="sc">{</span>(<span class="dv">1</span> <span class="op">-</span> alpha) <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %&quot;</span>, kosten, x,</span>
<span id="cb10-176"><a href="#cb10-176" aria-hidden="true" tabindex="-1"></a> quote_normal, sicherheit(x, echt)))</span>
<span id="cb10-177"><a href="#cb10-177" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-178"><a href="#cb10-178" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st"> In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren&quot;</span>)</span>
<span id="cb10-179"><a href="#cb10-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; rechnet richtig. Die Spalte &#39;echte Welt&#39; kommt in Teil (3).&quot;</span>)</span>
<span id="cb10-180"><a href="#cb10-180" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-181"><a href="#cb10-181" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-182"><a href="#cb10-182" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; (2) Was kostet ein Prozentpunkt Versorgungssicherheit?&quot;</span>)</span>
<span id="cb10-183"><a href="#cb10-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-184"><a href="#cb10-184" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;von -&gt; nach&#39;</span><span class="sc">:&lt;26}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Prozentpunkte&#39;</span><span class="sc">:&gt;13}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Mehrkosten&#39;</span><span class="sc">:&gt;13}</span><span class="ss"> &quot;</span></span>
<span id="cb10-185"><a href="#cb10-185" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;EUR je Punkt&#39;</span><span class="sc">:&gt;13}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-186"><a href="#cb10-186" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-187"><a href="#cb10-187" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> (n1, k1, q1), (n2, k2, q2) <span class="kw">in</span> <span class="bu">zip</span>(stufen, stufen[<span class="dv">1</span>:]):</span>
<span id="cb10-188"><a href="#cb10-188" aria-hidden="true" tabindex="-1"></a> d_punkte <span class="op">=</span> (q2 <span class="op">-</span> q1) <span class="op">*</span> <span class="dv">100</span></span>
<span id="cb10-189"><a href="#cb10-189" aria-hidden="true" tabindex="-1"></a> d_kosten <span class="op">=</span> k2 <span class="op">-</span> k1</span>
<span id="cb10-190"><a href="#cb10-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>n1 <span class="op">+</span> <span class="st">&#39; -&gt; &#39;</span> <span class="op">+</span> n2<span class="sc">:&lt;26}</span><span class="ss"> </span><span class="sc">{</span>d_punkte<span class="sc">:&gt;13.2f}</span><span class="ss"> </span><span class="sc">{</span>d_kosten<span class="sc">:&gt;13,.0f}</span><span class="ss"> &quot;</span></span>
<span id="cb10-191"><a href="#cb10-191" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>d_kosten <span class="op">/</span> d_punkte<span class="sc">:&gt;13,.0f}</span><span class="ss">&quot;</span>)</span>
<span id="cb10-192"><a href="#cb10-192" aria-hidden="true" tabindex="-1"></a> erst <span class="op">=</span> (stufen[<span class="dv">1</span>][<span class="dv">1</span>] <span class="op">-</span> stufen[<span class="dv">0</span>][<span class="dv">1</span>]) <span class="op">/</span> ((stufen[<span class="dv">1</span>][<span class="dv">2</span>] <span class="op">-</span> stufen[<span class="dv">0</span>][<span class="dv">2</span>]) <span class="op">*</span> <span class="dv">100</span>)</span>
<span id="cb10-193"><a href="#cb10-193" aria-hidden="true" tabindex="-1"></a> letzt <span class="op">=</span> (stufen[<span class="op">-</span><span class="dv">1</span>][<span class="dv">1</span>] <span class="op">-</span> stufen[<span class="op">-</span><span class="dv">2</span>][<span class="dv">1</span>]) <span class="op">/</span> ((stufen[<span class="op">-</span><span class="dv">1</span>][<span class="dv">2</span>] <span class="op">-</span> stufen[<span class="op">-</span><span class="dv">2</span>][<span class="dv">2</span>]) <span class="op">*</span> <span class="dv">100</span>)</span>
<span id="cb10-194"><a href="#cb10-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Der letzte Prozentpunkt kostet das </span><span class="sc">{</span>letzt <span class="op">/</span> erst<span class="sc">:.1f}</span><span class="ss">-fache des ersten.&quot;</span>)</span>
<span id="cb10-195"><a href="#cb10-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Sicherheit ist konvex bepreist - genau deshalb muss jemand&quot;</span>)</span>
<span id="cb10-196"><a href="#cb10-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; entscheiden, wie viel davon das Unternehmen kaufen will.&quot;</span>)</span>
<span id="cb10-197"><a href="#cb10-197" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-198"><a href="#cb10-198" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-199"><a href="#cb10-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; (3) Und wenn die Verteilung nicht normal ist?&quot;</span>)</span>
<span id="cb10-200"><a href="#cb10-200" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-201"><a href="#cb10-201" aria-hidden="true" tabindex="-1"></a> k_soc, x_soc <span class="op">=</span> plaene[<span class="fl">0.05</span>]</span>
<span id="cb10-202"><a href="#cb10-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in &quot;</span></span>
<span id="cb10-203"><a href="#cb10-203" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>P_KAELTEWELLE <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> % der Faelle liefern&quot;</span>)</span>
<span id="cb10-204"><a href="#cb10-204" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Wind </span><span class="sc">{</span>EINBRUCH[<span class="dv">1</span>] <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %, Sonne </span><span class="sc">{</span>EINBRUCH[<span class="dv">2</span>] <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> % &quot;</span></span>
<span id="cb10-205"><a href="#cb10-205" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;und - entscheidend - auch das Gaskraftwerk nur &quot;</span></span>
<span id="cb10-206"><a href="#cb10-206" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>EINBRUCH[<span class="dv">0</span>] <span class="op">*</span> <span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %&quot;</span>)</span>
<span id="cb10-207"><a href="#cb10-207" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;des Ueblichen. Alles bricht gleichzeitig ein.</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb10-208"><a href="#cb10-208" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-209"><a href="#cb10-209" aria-hidden="true" tabindex="-1"></a> a_bau <span class="op">=</span> ziehe_wetter(<span class="dv">400</span>, seed<span class="op">=</span><span class="dv">20260908</span>)</span>
<span id="cb10-210"><a href="#cb10-210" aria-hidden="true" tabindex="-1"></a> k_sz, x_sz <span class="op">=</span> chance_constraint_szenarien(a_bau, <span class="fl">0.05</span>)</span>
<span id="cb10-211"><a href="#cb10-211" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-212"><a href="#cb10-212" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(KOPF)</span>
<span id="cb10-213"><a href="#cb10-213" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb10-214"><a href="#cb10-214" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(zeile(<span class="st">&quot;SOC, Zusage 95 %&quot;</span>, k_soc, x_soc,</span>
<span id="cb10-215"><a href="#cb10-215" aria-hidden="true" tabindex="-1"></a> sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt)))</span>
<span id="cb10-216"><a href="#cb10-216" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(zeile(<span class="st">&quot;Szenarien, 95 %&quot;</span>, k_sz, x_sz,</span>
<span id="cb10-217"><a href="#cb10-217" aria-hidden="true" tabindex="-1"></a> sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt)))</span>
<span id="cb10-218"><a href="#cb10-218" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-219"><a href="#cb10-219" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Der SOC-Plan verspricht 95 % und haelt &quot;</span></span>
<span id="cb10-220"><a href="#cb10-220" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>sicherheit(x_soc, echt) <span class="op">*</span> <span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> %. Nicht das Verfahren ist&quot;</span>)</span>
<span id="cb10-221"><a href="#cb10-221" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; falsch, sondern die Annahme: In der Normalwelt liefert derselbe&quot;</span>)</span>
<span id="cb10-222"><a href="#cb10-222" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Plan </span><span class="sc">{</span>sicherheit(x_soc, normalwelt) <span class="op">*</span> <span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> %.&quot;</span>)</span>
<span id="cb10-223"><a href="#cb10-223" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Der Szenarioplan (</span><span class="sc">{</span><span class="bu">len</span>(a_bau)<span class="sc">}</span><span class="ss"> Szenarien, </span><span class="sc">{</span><span class="bu">len</span>(a_bau)<span class="sc">}</span><span class="ss"> &quot;</span></span>
<span id="cb10-224"><a href="#cb10-224" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Binaervariablen) kommt auf&quot;</span>)</span>
<span id="cb10-225"><a href="#cb10-225" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>sicherheit(x_sz, echt) <span class="op">*</span> <span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> % - und kostet dafuer &quot;</span></span>
<span id="cb10-226"><a href="#cb10-226" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>(k_sz <span class="op">/</span> k_soc <span class="op">-</span> <span class="dv">1</span>) <span class="op">*</span> <span class="dv">100</span><span class="sc">:+.1f}</span><span class="ss"> %. Er kauft keine Windkraft mehr:&quot;</span>)</span>
<span id="cb10-227"><a href="#cb10-227" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Was im Ernstfall ausfaellt, hilft der Zusage nicht.&quot;</span>)</span>
<span id="cb10-228"><a href="#cb10-228" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>==============================================================================
WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK
==============================================================================
Gesichert bereitzustellen: 500 MW
Technologie EUR/MW verfuegbar Streuung Grenze EUR je erw. MW
------------------------------------------------------------------------------
Gaskraftwerk 65,000 0.92 0.05 400 70,652
Windpark 24,000 0.35 0.16 250 68,571
Solarpark 12,000 0.18 0.10 600 66,667
Biomasse 60,000 0.85 0.04 350 70,588
Vollausbau liefert im Mittel 861 MW, in der Kaeltewelle 552 MW.
==============================================================================
(1) Mittelwertplan und Chance Constraints im Vergleich
==============================================================================
Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt
------------------------------------------------------------------------------
Mittelwert 34,694,565 8 250 600 350 50.08 % 46.18 %
Zusage 80 % 36,382,277 226 25 44 343 80.06 % 73.61 %
Zusage 90 % 36,994,777 233 20 35 349 89.99 % 82.82 %
Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 %
Zusage 99 % 38,529,698 258 18 28 350 98.97 % 91.05 %
In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren
rechnet richtig. Die Spalte &#39;echte Welt&#39; kommt in Teil (3).
==============================================================================
(2) Was kostet ein Prozentpunkt Versorgungssicherheit?
==============================================================================
von -&gt; nach Prozentpunkte Mehrkosten EUR je Punkt
------------------------------------------------------------------------------
Mittelwert -&gt; Zusage 80 % 29.98 1,687,712 56,289
Zusage 80 % -&gt; Zusage 90 % 9.93 612,500 61,685
Zusage 90 % -&gt; Zusage 95 % 4.98 519,530 104,365
Zusage 95 % -&gt; Zusage 99 % 4.00 1,015,390 253,848
Der letzte Prozentpunkt kostet das 4.5-fache des ersten.
Sicherheit ist konvex bepreist - genau deshalb muss jemand
entscheiden, wie viel davon das Unternehmen kaufen will.
==============================================================================
(3) Und wenn die Verteilung nicht normal ist?
==============================================================================
Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in 8 % der Faelle liefern
Wind 10 %, Sonne 15 % und - entscheidend - auch das Gaskraftwerk nur 72 %
des Ueblichen. Alles bricht gleichzeitig ein.
Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt
------------------------------------------------------------------------------
SOC, Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 %
Szenarien, 95 % 43,760,709 350 0 0 350 100.00 % 94.84 %
Der SOC-Plan verspricht 95 % und haelt 87.4 %. Nicht das Verfahren ist
falsch, sondern die Annahme: In der Normalwelt liefert derselbe
Plan 95.0 %.
Der Szenarioplan (400 Szenarien, 400 Binaervariablen) kommt auf
94.8 % - und kostet dafuer +16.7 %. Er kauft keine Windkraft mehr:
Was im Ernstfall ausfaellt, hilft der Zusage nicht.
==============================================================================</code></pre>
<p><strong>Drei Befunde, die man dem Plan nicht ansieht.</strong></p>
<p><strong>Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus.</strong> Das ist kein Fehler, sondern die Definition des Erwartungswerts — und dieselbe Falle wie in <a href="#sec:unsicherheit-der-fluch-des-durchschnitts">Abschnitt 12.3</a>, nur teurer. Er kauft die billigsten Quellen je <em>erwarteter</em> Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, Wind und Sonne schrumpfen auf einen Rest.</p>
<p><strong>Zweitens: Sicherheit ist konvex bepreist.</strong> Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das <strong>4,5-fache</strong>. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der <em>nächste</em> Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.</p>
<p><strong>Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung.</strong> Der Plan verspricht 95 % und hält <strong>87,4 %</strong>. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht <strong>alles zugleich</strong> ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.</p>
<p>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 <strong>keine Windkraft mehr</strong>. Die Logik dahinter ist hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.</p>
<blockquote>
<p><strong>⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich</strong> Bei <span class="math inline">S</span> Szenarien und <span class="math inline">\alpha = 5\,\%</span> darf das Modell genau <span class="math inline">0{,}05 \cdot S</span> davon ignorieren — und es sucht sich die <strong>teuersten</strong> aus. In der Stichprobe trifft es seine Quote dadurch immer; außerhalb streut sie.</p>
<p>Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei <span class="math inline">S = 200</span>, <span class="math inline">400</span>, <span class="math inline">800</span>, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen <strong>93,1 % und 96,7 %</strong>. Die Spanne wurde von <span class="math inline">S = 200</span> auf <span class="math inline">400</span> enger (3,5 → 1,2 Prozentpunkte), bei <span class="math inline">S = 800</span> aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht.</p>
<p><strong>Konsequenz für die Praxis:</strong> Eine mit Szenarien erkaufte Zusage immer an einer <strong>zurückgehaltenen</strong> Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in <a href="handelsmaschine.html#kap-handelsmaschine">Kapitel 21</a>.</p>
</blockquote>
<blockquote>
<p><strong>🎯 Merksatz</strong> Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern.</p>
</blockquote>
<hr />
<h2 id="sec:unsicherheit-uebungsaufgaben">12.8 Übungsaufgaben</h2>
<blockquote>
<p>Lösungen: <a href="anhang-loesungen.html#sec:loesungen-unsicherheit">Abschnitt A.12</a>.</p>
</blockquote>
<p><strong>Aufgabe 12.1 ⭐ — Fluch des Durchschnitts erkennen.</strong> Ein Bauunternehmer plant mit der <em>durchschnittlichen</em> Bauzeit von 8 Monaten. Warum ist die <em>erwartete</em> Fertigstellung trotzdem später als 8 Monate, wenn Verzögerungen wahrscheinlicher sind als Beschleunigungen? Nennen Sie zwei weitere Alltagsbeispiele.</p>
<p><strong>Aufgabe 12.2 ⭐ — Ansatz wählen.</strong> Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? (e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält?</p>
<p><strong>Aufgabe 12.3 ⭐⭐ — Zweistufiges Modell rechnen.</strong> Wiederholen Sie die Handrechnung <em>Warum der Mittelwert in die Irre führt</em> mit geänderten Kosten: Spot 60 € statt 120 €. (a) Wie ändert sich die optimale Kapazität? (b) Erklären Sie die Richtung der Änderung. (c) Bei welchem Spot-Preis wäre die Mittelwert-Planung optimal?</p>
<p><strong>Aufgabe 12.4 ⭐⭐ — EVPI interpretieren.</strong> Ihr Modell liefert EVPI = 8 400 € pro Jahr. Ein Anbieter verlangt 15 000 € jährlich für eine Prognoselösung, die „80 % Treffsicherheit“ verspricht. Wie argumentieren Sie?</p>
<p><strong>Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern.</strong> Erweitern Sie <code>Monte_Carlo.py</code>: (a) Fügen Sie ein Servicelevel-Kriterium hinzu: „Die Unterdeckung darf höchstens in 5 % der Fälle auftreten.“ Welche Kapazität ist dafür nötig, und was kostet sie zusätzlich? (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten.</p>
<p><strong>Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen.</strong> <code>Chance_Constraints.py</code> beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das <strong>szenariobasierte</strong> Modell meldet dann bei 95 % <code>infeasible</code>, das <strong>analytische</strong> dagegen <code>optimal</code>. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten.</p>
<p><strong>Aufgabe 12.7 ⭐⭐⭐ — Budgeted Uncertainty.</strong> Erweitern Sie <code>Robuste_Optimierung.py</code> um den Ansatz von Bertsimas/Sim: Höchstens <span class="math inline">\Gamma</span> Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie <span class="math inline">\Gamma \in \{0, 1, 2, 3, 4\}</span> und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei <span class="math inline">\Gamma</span> ganzzahlig genügt es, die <span class="math inline">\Gamma</span> größten <span class="math inline">\delta_i w_i</span> abzuziehen — das lässt sich in CVXPY mit <code>cp.sum_largest</code> formulieren.)</p>
<hr />
<h2 id="sec:unsicherheit-denkfehler">12.9 Finde den Denkfehler</h2>
<div class="card card-denkfehler">
<blockquote>
<p><strong>🐛 Finde den Denkfehler: Warum jedes Projekt zu spät fertig wird</strong></p>
<p>Ein Projektleiter plant eine Umbaumaßnahme. Fünf Gewerke arbeiten <strong>parallel</strong> und unabhängig voneinander; fertig ist der Umbau, wenn das letzte fertig ist. Für jedes Gewerk liegt eine Schätzung vor: mindestens 6 Tage, wahrscheinlich 10, im schlechtesten Fall 18.</p>
<p>Er rechnet: <em>„Jedes Gewerk braucht im Mittel <span class="math inline">(6 + 10 + 18)/3 = 11{,}3</span> Tage. Alle laufen gleichzeitig. Also ist der Umbau nach 11,3 Tagen fertig — ich plane großzügig 12 ein.“</em></p>
<p>Nachgerechnet mit 200 000 simulierten Umbauten:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>rng <span class="op">=</span> np.random.default_rng(<span class="dv">3</span>)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>dauer <span class="op">=</span> rng.triangular(<span class="dv">6</span>, <span class="dv">10</span>, <span class="dv">18</span>, size<span class="op">=</span>(<span class="dv">200_000</span>, <span class="dv">5</span>)) <span class="co"># 5 Gewerke, je eine Schaetzung</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>projekt <span class="op">=</span> dauer.<span class="bu">max</span>(axis<span class="op">=</span><span class="dv">1</span>) <span class="co"># fertig, wenn das LETZTE fertig ist</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Mittelwert einer Einzeldauer: </span><span class="sc">{</span>dauer<span class="sc">.</span>mean()<span class="sc">:.2f}</span><span class="ss"> Tage&quot;</span>)</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Tatsaechliche Projektdauer: </span><span class="sc">{</span>projekt<span class="sc">.</span>mean()<span class="sc">:.2f}</span><span class="ss"> Tage&quot;</span>)</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;90-%-Quantil: </span><span class="sc">{</span>np<span class="sc">.</span>quantile(projekt, <span class="fl">0.9</span>)<span class="sc">:.2f}</span><span class="ss"> Tage&quot;</span>)</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f&quot;Anteil ueber 11,34 Tage: </span><span class="sc">{</span>(projekt <span class="op">&gt;</span> <span class="fl">11.34</span>)<span class="sc">.</span>mean() <span class="op">*</span> <span class="dv">100</span><span class="sc">:.1f}</span><span class="ss"> %&quot;</span>)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>Mittelwert einer Einzeldauer: 11.34 Tage
Tatsaechliche Projektdauer: 14.38 Tage
90-%-Quantil: 16.59 Tage
Anteil ueber 11,34 Tage: 95.5 %</code></pre>
<p><strong>Ihre Aufgabe:</strong> (a) Der Projektleiter hat richtig gerechnet — jedes Gewerk braucht tatsächlich im Mittel 11,34 Tage. Warum dauert das Projekt trotzdem 14,38 Tage? (b) Erklären Sie in einem Satz, warum ausgerechnet <strong>95,5 %</strong> aller Umbauten den Plan reißen. Was müsste gelten, damit es 50 % wären? (c) Was passiert mit der Projektdauer, wenn statt fünf Gewerken zehn parallel arbeiten — bei unveränderter Einzelschätzung? (d) Welche Zahl gehört in den Projektplan, wenn der Auftraggeber eine Terminzusage mit 90 % Sicherheit verlangt?</p>
<p><em>Auflösung: <a href="anhang-loesungen.html#sec:loesungen-unsicherheit">Abschnitt A.12</a>.</em></p>
</blockquote>
</div>
<blockquote>
<p><strong>🎯 Merksatz</strong> Der Fluch des Durchschnitts hat zwei Gesichter. Beim Ersatzteil aus <a href="#sec:unsicherheit-schnellstart">Abschnitt 12.1</a> führt der Mittelwert in die Irre, weil die <strong>Kosten</strong> asymmetrisch sind. Hier führt er in die Irre, weil die <strong>Verknüpfung</strong> asymmetrisch ist: Ein Gewerk, das früher fertig wird, hilft niemandem — ein Gewerk, das sich verspätet, verzögert alles. Beide Male gilt: <em>Der Mittelwert einer Funktion ist nicht die Funktion des Mittelwerts.</em></p>
</blockquote>
<hr />
<h2 id="sec:unsicherheit-quiz">12.10 Micro-Quiz</h2>
<div class="card card-quiz">
<blockquote>
<p><strong>❓ Micro-Quiz 12: Drei Fragen zum Selbstcheck</strong></p>
<p>Genau eine Antwort ist jeweils richtig. Auflösung in <a href="anhang-loesungen.html#quiz-loesung-unsicherheit">Anhang A</a>.</p>
<p><strong>1. Bei einem Ersatzteil kostet ein Stück zu wenig 1 400 €, ein Stück zu viel 120 €. Der Bedarf beträgt im Mittel 30 Stück. Wie viel bestellen Sie?</strong> (a) 30 — das ist der Erwartungswert des Bedarfs. (b) Deutlich mehr als 30, nämlich das 92,1-%-Quantil des Bedarfs. Das kritische Verhältnis <span class="math inline">1400/(1400+120)</span> sagt, wie weit man sich auf die günstigere Fehlerseite stellen soll. (c) Weniger als 30, weil Lagerhaltung Kapital bindet.</p>
<p><strong>2. Was unterscheidet </strong>stochastische** von <strong>robuster</strong> Optimierung?** (a) Stochastische Optimierung ist genauer, robuste ist eine Näherung für schnelle Rechnungen. (b) Stochastische Optimierung braucht <strong>Wahrscheinlichkeiten</strong> und optimiert den Erwartungswert; robuste Optimierung braucht nur <strong>Bandbreiten</strong> und sichert den schlechtesten Fall darin ab. (c) Robuste Optimierung berücksichtigt mehr Szenarien.</p>
<p><strong>3. Ein Kollege ersetzt in seinem Modell alle unsicheren Größen durch ihre Mittelwerte und rechnet deterministisch. Wann ist das unproblematisch?</strong> (a) Immer — der Erwartungswert ist die beste Einzelschätzung. (b) Nie. (c) Wenn Zielfunktion und Nebenbedingungen in den unsicheren Größen <strong>linear</strong> sind und keine Entscheidung erst nach Beobachtung fällt. Sobald Minimum, Maximum, Betrag oder eine Nachbesserungsentscheidung im Spiel ist, gilt es nicht mehr.</p>
</blockquote>
</div>
<hr />
<h2 id="sec:unsicherheit-selbsttest">12.11 Selbsttest</h2>
<blockquote>
<p>Antworten: <a href="anhang-loesungen.html#selbsttest-loesung-unsicherheit">Anhang A</a>.</p>
</blockquote>
<ol type="1">
<li>Warum ist Planung mit Erwartungswerten bei asymmetrischen Kosten systematisch falsch?</li>
<li>Was unterscheidet Stufe-1- von Stufe-2-Variablen?</li>
<li>Was misst der EVPI, und wofür ist er praktisch nützlich?</li>
<li>Warum braucht robuste Optimierung keine Wahrscheinlichkeiten?</li>
<li>Wie macht man ein Min-Max-Problem mit Box-Unsicherheit lösbar?</li>
<li>Warum wird aus <span class="math inline">\mathbb{P}(\mathbf{a}^\top\mathbf{x} \ge b) \ge 1-\alpha</span> unter Normalverteilung eine <strong>Norm</strong> und nicht ein fester Zuschlag je Variable?</li>
<li>Ein Plan mit Chance Constraint verspricht 95 % und hält gemessen 87 %. Woran liegt das — und was ändert die szenariobasierte Formulierung daran?</li>
</ol>
<hr />
<h2 id="sec:unsicherheit-zusammenfassung">12.12 Zusammenfassung</h2>
<ul>
<li><strong>Der Fluch des Durchschnitts</strong> ist kein Randphänomen und hat zwei Gesichter. Bei asymmetrischen <strong>Kosten</strong> liegt das Optimum systematisch neben dem Mittelwert — das Newsvendor-Verhältnis <span class="math inline">c_{-}/(c_{-}+c_{+})</span> sagt, wie weit. Bei asymmetrischer <strong>Verknüpfung</strong> (Maximum über parallele Vorgänge) ist der Erwartungswert des Ganzen größer als das Ganze der Erwartungswerte. Beide Male gilt: Der Mittelwert einer Funktion ist nicht die Funktion des Mittelwerts (Jensensche Ungleichung).</li>
<li><strong>Eine Terminzusage ist ein Quantil, kein Mittelwert.</strong> Wer den Erwartungswert zusagt, reißt den Termin bei fünf parallelen Vorgängen in 95 % der Fälle.</li>
<li><strong>Monte-Carlo</strong> bewertet, optimiert aber nicht. Sein Wert liegt in Kennzahlen, für die es keine Formel gibt.</li>
<li><strong>Zweistufige stochastische Programme</strong> trennen die Festlegung von der Reaktion — ein <span class="math inline">\mathbf{x}</span>, aber je Szenario ein <span class="math inline">\mathbf{y}_s</span>.</li>
<li><strong>Der EVPI</strong> begrenzt, was eine perfekte Prognose wert sein darf.</li>
<li><strong>Robuste Optimierung</strong> braucht keine Wahrscheinlichkeiten, nur eine Unsicherheitsmenge — und wird für einfache Mengen zu einem gewöhnlichen Problem mit Abzugsterm.</li>
<li><strong>Chance Constraints</strong> schreiben die Zusage selbst ins Modell. Unter Normalverteilung wird daraus eine <strong>Kegelbedingung</strong> (der Sicherheitszuschlag ist eine Norm, deshalb belohnt sie Mischung), szenariobasiert ein <strong>MILP mit Big-M</strong> ohne jede Verteilungsannahme.</li>
<li><strong>Sicherheit ist konvex bepreist.</strong> Im Beispiel kostet der letzte Prozentpunkt das 4,5-fache des ersten. Die richtige Managementfrage lautet nicht „was kostet Sicherheit“, sondern „was kostet der <em>nächste</em> Prozentpunkt“.</li>
<li><strong>Eine Zusage gilt nur für die unterstellte Verteilung.</strong> Ein Plan, der unter Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle nicht kannte, in der <em>alles</em> zugleich ausfällt. Szenariobasierte Zusagen wiederum überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden.</li>
<li><strong>Beziffern Sie immer Preis und Nutzen der Absicherung.</strong> Ohne beide Zahlen ist die Entscheidung nicht begründbar.</li>
</ul>
<p><strong>Ausblick.</strong> <a href="dynamische-programmierung.html#kap-dynamische-programmierung">Kapitel 13</a> fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.</p>
</article>
<button type="button" class="fortschritt-knopf" data-kapitel="unsicherheit.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="qp-nlp.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a><a class="prev-next-knopf prev-next-next" href="dynamische-programmierung.html"><span><small>Weiter</small>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
</main>
</div>
<footer class="site-footer">
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
<a href="programme.html">Beispielprogramme</a></p>
</footer>
<script defer src="assets/search-index.js"></script>
<script defer src="assets/programme.js"></script>
<script defer src="assets/site.js"></script>
</body>
</html>