920 lines
125 KiB
HTML
920 lines
125 KiB
HTML
|
|
<!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></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></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-ch
|
|||
|
|
<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 drei 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 — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht.</p>
|
|||
|
|
<p><strong>Zeitbedarf:</strong> ca. 5,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></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">"Optimum:"</span>, kandidaten[werte.argmin()], <span class="st">"Stueck,"</span>, <span class="ss">f"</span><span class="sc">{</span>werte<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:,.0f}</span><span class="ss"> EUR"</span>)</span>
|
|||
|
|
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">"Mittlerer Bedarf 30:"</span>, <span class="ss">f"</span><span class="sc">{</span>kosten(<span class="dv">30</span>)<span class="sc">:,.0f}</span><span class="ss"> EUR"</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>… begründen, welcher der drei 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">"""</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">"""</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">>=</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">"__main__"</span>:</span>
|
|||
|
|
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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">" DER FLUCH DES DURCHSCHNITTS"</span>)</span>
|
|||
|
|
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"Erwarteter Bedarf (Mittelwert): </span><span class="sc">{</span>ERWARTUNGSWERT<span class="sc">:.1f}</span><span class="ss">"</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"Kosten bei naiver Planung x=</span><span class="sc">{</span>ERWARTUNGSWERT<span class="sc">:.0f}</span><span class="ss">: "</span></span>
|
|||
|
|
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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">"</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"Optimales x (durch Scan gefunden): </span><span class="sc">{</span>x_optimal<span class="sc">}</span><span class="ss">"</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"Kosten beim Optimum: </span><span class="sc">{</span>kosten_optimal<span class="sc">:,.2f}</span><span class="ss"> EUR"</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"Ersparnis gegenueber naiver Planung: "</span></span>
|
|||
|
|
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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">"</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">"Kosten fuer ausgewaehlte x zum Vergleich:"</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">" <- Mittelwert"</span> <span class="cf">if</span> x <span class="op">==</span> <span class="dv">225</span> <span class="cf">else</span> (<span class="st">" <- Optimum"</span> <span class="cf">if</span> x <span class="op">==</span> <span class="dv">250</span> <span class="cf">else</span> <span class="st">""</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" 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">:>12,.2f}</span><span class="ss"> EUR</span><span class="sc">{</span>markierung<span class="sc">}</span><span class="ss">"</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">"</span><span class="ch">\n</span><span class="st">Das Optimum liegt exakt auf einem Szenariowert (250 = 'Volatil'),"</span>)</span>
|
|||
|
|
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen."</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 <- Mittelwert
|
|||
|
|
x= 250: 16,375.00 EUR <- Optimum
|
|||
|
|
x= 300: 17,375.00 EUR
|
|||
|
|
x= 500: 21,375.00 EUR
|
|||
|
|
|
|||
|
|
Das Optimum liegt exakt auf einem Szenariowert (250 = 'Volatil'),
|
|||
|
|
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-drei-ansätze-im-überblick">Die drei 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: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
</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>
|
|||
|
|
</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>
|
|||
|
|
</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>
|
|||
|
|
</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>
|
|||
|
|
</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>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>Schwäche</td>
|
|||
|
|
<td>keine Optimierung</td>
|
|||
|
|
<td>Wahrscheinlichkeiten müssen stimmen</td>
|
|||
|
|
<td>oft übervorsichtig, kostet Ertrag</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td>Kapitel</td>
|
|||
|
|
<td>9.2</td>
|
|||
|
|
<td>9.3</td>
|
|||
|
|
<td>9.4</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<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">"""</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">"""</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">"""</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"> """</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"><</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">"""Gesamtkosten je Szenario fuer eine gegebene Vorabkapazitaet."""</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">"__main__"</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">"="</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" MONTE-CARLO-BEWERTUNG (</span><span class="sc">{</span>ANZAHL_ZIEHUNGEN<span class="sc">:,}</span><span class="ss"> Szenarien)"</span>)</span>
|
|||
|
|
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"Bedarfsverteilung: Mittelwert </span><span class="sc">{</span>bedarf<span class="sc">.</span>mean()<span class="sc">:.1f}</span><span class="ss"> | "</span></span>
|
|||
|
|
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Median </span><span class="sc">{</span>np<span class="sc">.</span>median(bedarf)<span class="sc">:.1f}</span><span class="ss"> | "</span></span>
|
|||
|
|
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"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"> | "</span></span>
|
|||
|
|
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Maximum </span><span class="sc">{</span>bedarf<span class="sc">.</span><span class="bu">max</span>()<span class="sc">:.1f}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Der Median liegt deutlich unter dem Mittelwert - die Verteilung ist"</span>)</span>
|
|||
|
|
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"rechtsschief. Genau hier fuehrt Planung mit dem Mittelwert in die Irre.</span><span class="ch">\n</span><span class="st">"</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"</span><span class="sc">{</span><span class="st">'Kapazitaet'</span><span class="sc">:>10}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">'Erw. Kosten'</span><span class="sc">:>12}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">'Median'</span><span class="sc">:>10}</span><span class="ss"> | "</span></span>
|
|||
|
|
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'95%-Quantil'</span><span class="sc">:>12}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">'Unterdeckung'</span><span class="sc">:>12}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</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">></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"</span><span class="sc">{</span>kapazitaet<span class="sc">:>10}</span><span class="ss"> | </span><span class="sc">{</span>kosten<span class="sc">.</span>mean()<span class="sc">:>12,.0f}</span><span class="ss"> | "</span></span>
|
|||
|
|
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>np<span class="sc">.</span>median(kosten)<span class="sc">:>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">:>12,.0f}</span><span class="ss"> | "</span></span>
|
|||
|
|
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>p_unterdeckung<span class="op">*</span><span class="dv">100</span><span class="sc">:>11.1f}</span><span class="ss"> %"</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">"-"</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"Bester Kandidat: Kapazitaet </span><span class="sc">{</span>beste[<span class="dv">0</span>]<span class="sc">}</span><span class="ss"> mit erwarteten Kosten "</span></span>
|
|||
|
|
<span id="cb5-74"><a href="#cb5-74" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>beste[<span class="dv">1</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR"</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"Feinsuche (Raster 100..500): Optimum bei Kapazitaet </span><span class="sc">{</span>optimum<span class="sc">}</span><span class="ss">, "</span></span>
|
|||
|
|
<span id="cb5-81"><a href="#cb5-81" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"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"</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">"</span><span class="ch">\n</span><span class="st">--- Fluch des Durchschnitts, gemessen ---"</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" 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"</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" 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"</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" 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 "</span></span>
|
|||
|
|
<span id="cb5-91"><a href="#cb5-91" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(</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"> %)"</span>)</span>
|
|||
|
|
<span id="cb5-92"><a href="#cb5-92" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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 (Übung 9.5).</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} &\le \mathbf{b} && \text{(Stufe-1-Restriktionen)}\\
|
|||
|
|
\mathbf{T}_s\mathbf{x} + \mathbf{W}_s\mathbf{y}_s &\le \mathbf{h}_s \quad \forall s
|
|||
|
|
&& \text{(Kopplung je Szenario)}\\
|
|||
|
|
\mathbf{x} \ge \mathbf{0},\ \mathbf{y}_s &\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">"""</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">"""</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">"Ruhig"</span>, <span class="st">"Volatil"</span>, <span class="st">"Crash"</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">"""Zweistufiges Modell: eine Vorabentscheidung, szenarioabhaengige Korrektur."""</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">"Basiskapazitaet"</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">"Spot_Zukauf"</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">"Leerlauf"</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">"""Erwartete Gesamtkosten fuer eine fest vorgegebene Kapazitaet."""</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">"__main__"</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">"="</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">" STOCHASTISCHE TWO-STAGE OPTIMIERUNG (KAPAZITAETSPLANUNG)"</span>)</span>
|
|||
|
|
<span id="cb7-62"><a href="#cb7-62" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"Status: </span><span class="sc">{</span>problem<span class="sc">.</span>status<span class="sc">}</span><span class="ss">"</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"Erwarteter Bedarf (Mittelwert): </span><span class="sc">{</span>mittelwert<span class="sc">:.1f}</span><span class="ss"> Einheiten"</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"Optimale Stufe-1-Kapazitaet x*: </span><span class="sc">{</span>kapazitaet<span class="sc">:.1f}</span><span class="ss"> Einheiten"</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"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">"</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">"Szenario"</span>: SZENARIEN,</span>
|
|||
|
|
<span id="cb7-70"><a href="#cb7-70" aria-hidden="true" tabindex="-1"></a> <span class="st">"Wahrsch."</span>: [<span class="ss">f"</span><span class="sc">{</span>p<span class="op">*</span><span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %"</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">"Bedarf"</span>: BEDARF,</span>
|
|||
|
|
<span id="cb7-72"><a href="#cb7-72" aria-hidden="true" tabindex="-1"></a> <span class="st">"Basis genutzt"</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">"Spot-Zukauf"</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">"Leerlauf"</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">"Kosten (EUR)"</span>: [<span class="ss">f"</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">"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</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">"Vergleich verschiedener Planungsstrategien:"</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"</span><span class="sc">{</span><span class="st">'Strategie'</span><span class="sc">:<34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kapazitaet'</span><span class="sc">:>11}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Erw. Kosten'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Mehrkosten'</span><span class="sc">:>13}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb7-84"><a href="#cb7-84" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</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">"Stochastisch optimal"</span>, kapazitaet),</span>
|
|||
|
|
<span id="cb7-89"><a href="#cb7-89" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Naiv: Mittelwert einsetzen"</span>, mittelwert),</span>
|
|||
|
|
<span id="cb7-90"><a href="#cb7-90" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Vorsichtig: Worst Case abdecken"</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">"Optimistisch: Bestfall"</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"</span><span class="sc">{</span>name<span class="sc">:<34}</span><span class="ss"> </span><span class="sc">{</span>kap<span class="sc">:>11.1f}</span><span class="ss"> </span><span class="sc">{</span>kosten<span class="sc">:>14,.0f}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb7-96"><a href="#cb7-96" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>kosten <span class="op">-</span> optimal<span class="sc">:>+13,.0f}</span><span class="ss">"</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">"-"</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"Kosten bei perfekter Voraussicht: </span><span class="sc">{</span>kosten_perfekt<span class="sc">:>10,.0f}</span><span class="ss"> EUR"</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"Wert perfekter Information (EVPI): </span><span class="sc">{</span>evpi<span class="sc">:>10,.0f}</span><span class="ss"> EUR "</span></span>
|
|||
|
|
<span id="cb7-105"><a href="#cb7-105" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(</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)"</span>)</span>
|
|||
|
|
<span id="cb7-106"><a href="#cb7-106" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" -> So viel duerfte eine perfekte Bedarfsprognose hoechstens kosten."</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">"-"</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">></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"Analyse: Der Solver waehlt </span><span class="sc">{</span>kapazitaet<span class="sc">:.0f}</span><span class="ss"> Einheiten und damit MEHR als"</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"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)"</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"deutlich teurer ist als Leerlauf (</span><span class="sc">{</span>KOSTEN_LEERLAUF<span class="sc">:.0f}</span><span class="ss"> EUR)."</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"><</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"Analyse: Der Solver waehlt </span><span class="sc">{</span>kapazitaet<span class="sc">:.0f}</span><span class="ss"> und damit WENIGER als den"</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"Mittelwert (</span><span class="sc">{</span>mittelwert<span class="sc">:.0f}</span><span class="ss">) - Leerlauf ist hier teurer als Zukauf."</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">"Analyse: Kapazitaet entspricht dem Mittelwert (symmetrische Kosten)."</span>)</span>
|
|||
|
|
<span id="cb7-119"><a href="#cb7-119" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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)
|
|||
|
|
-> 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 Übung 9.4.</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">"""</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 "Preis der Robustheit": 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">"""</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">"Aktien Welt"</span>, <span class="st">"Anleihen"</span>, <span class="st">"Rohstoffe"</span>, <span class="st">"Immobilien"</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">"""Standard-Mean-Variance mit vorgegebenem Renditevektor."""</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">"__main__"</span>:</span>
|
|||
|
|
<span id="cb9-50"><a href="#cb9-50" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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">" ROBUSTE vs. NOMINALE PORTFOLIO-OPTIMIERUNG"</span>)</span>
|
|||
|
|
<span id="cb9-52"><a href="#cb9-52" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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"</span><span class="sc">{</span><span class="st">'Asset'</span><span class="sc">:<14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Erw. Rendite'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Unsicherheit'</span><span class="sc">:>14}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb9-54"><a href="#cb9-54" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Worst Case'</span><span class="sc">:>12}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Volatilitaet'</span><span class="sc">:>13}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb9-55"><a href="#cb9-55" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</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"</span><span class="sc">{</span>name<span class="sc">:<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">:>13.1f}</span><span class="ss"> % "</span></span>
|
|||
|
|
<span id="cb9-58"><a href="#cb9-58" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'+/- '</span> <span class="op">+</span> <span class="bu">format</span>(UNSICHERHEIT[i]<span class="op">*</span><span class="dv">100</span>, <span class="st">'.1f'</span>) <span class="op">+</span> <span class="st">' %'</span><span class="sc">:>14}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb9-59"><a href="#cb9-59" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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">:>11.1f}</span><span class="ss"> % "</span></span>
|
|||
|
|
<span id="cb9-60"><a href="#cb9-60" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>VOLA[i]<span class="op">*</span><span class="dv">100</span><span class="sc">:>12.1f}</span><span class="ss"> %"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</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"</span><span class="sc">{</span><span class="st">''</span><span class="sc">:<14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'nominal'</span><span class="sc">:>22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'robust'</span><span class="sc">:>22}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb9-69"><a href="#cb9-69" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</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"</span><span class="sc">{</span>name<span class="sc">:<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">:>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">:>21.1f}</span><span class="ss"> %"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</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"</span><span class="sc">{</span><span class="st">'Bewertung'</span><span class="sc">:<34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'nominales Portfolio'</span><span class="sc">:>22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'robustes Portfolio'</span><span class="sc">:>22}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb9-82"><a href="#cb9-82" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"-"</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"</span><span class="sc">{</span><span class="st">'Ertrag, wenn Schaetzung stimmt'</span><span class="sc">:<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">:>21.2f}</span><span class="ss"> % "</span></span>
|
|||
|
|
<span id="cb9-84"><a href="#cb9-84" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>e_rob_gut<span class="op">*</span><span class="dv">100</span><span class="sc">:>21.2f}</span><span class="ss"> %"</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"</span><span class="sc">{</span><span class="st">'Ertrag im Worst Case'</span><span class="sc">:<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">:>21.2f}</span><span class="ss"> % "</span></span>
|
|||
|
|
<span id="cb9-86"><a href="#cb9-86" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>e_rob_schlecht<span class="op">*</span><span class="dv">100</span><span class="sc">:>21.2f}</span><span class="ss"> %"</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"</span><span class="sc">{</span><span class="st">'Volatilitaet'</span><span class="sc">:<34}</span><span class="ss"> </span><span class="sc">{</span>r_nom<span class="op">*</span><span class="dv">100</span><span class="sc">:>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">:>21.2f}</span><span class="ss"> %"</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">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</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"Preis der Robustheit (Ertragsverzicht im Normalfall): "</span></span>
|
|||
|
|
<span id="cb9-91"><a href="#cb9-91" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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"</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"Nutzen der Robustheit (Vorteil im Worst Case): "</span></span>
|
|||
|
|
<span id="cb9-93"><a href="#cb9-93" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</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"</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"Verhaeltnis Nutzen/Preis: </span><span class="sc">{</span>verhaeltnis<span class="sc">:.2f}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb9-97"><a href="#cb9-97" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" -> Werte > 1 bedeuten: Die Absicherung bringt im Ernstfall mehr,"</span>)</span>
|
|||
|
|
<span id="cb9-98"><a href="#cb9-98" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" als sie im Normalfall kostet."</span>)</span>
|
|||
|
|
<span id="cb9-99"><a href="#cb9-99" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</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-uebungsaufgaben">12.7 Ü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 drei 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?</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 ⭐⭐⭐ — 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.8 Finde den Denkfehler</h2>
|
|||
|
|
<div class="card card-denkfehler">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🐛 Finde den Denkfehler 9.1: 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="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="im">import</span> numpy <span class="im">as</span> np</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>rng <span class="op">=</span> np.random.default_rng(<span class="dv">3</span>)</span>
|
|||
|
|
<span id="cb10-4"><a href="#cb10-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="cb10-5"><a href="#cb10-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="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="bu">print</span>(<span class="ss">f"Mittelwert einer Einzeldauer: </span><span class="sc">{</span>dauer<span class="sc">.</span>mean()<span class="sc">:.2f}</span><span class="ss"> Tage"</span>)</span>
|
|||
|
|
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"Tatsaechliche Projektdauer: </span><span class="sc">{</span>projekt<span class="sc">.</span>mean()<span class="sc">:.2f}</span><span class="ss"> Tage"</span>)</span>
|
|||
|
|
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"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"</span>)</span>
|
|||
|
|
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"Anteil ueber 11,34 Tage: </span><span class="sc">{</span>(projekt <span class="op">></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"> %"</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.9 Micro-Quiz</h2>
|
|||
|
|
<div class="card card-quiz">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>❓ Micro-Quiz 9: 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.10 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>
|
|||
|
|
</ol>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:unsicherheit-zusammenfassung">12.11 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>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>
|