diff --git a/Notebooks_04/unsicherheit.ipynb b/Notebooks_04/unsicherheit.ipynb index 2d53647..82b5421 100644 --- a/Notebooks_04/unsicherheit.ipynb +++ b/Notebooks_04/unsicherheit.ipynb @@ -451,6 +451,251 @@ " print(\" als sie im Normalfall kostet.\")\n", " print(\"=\" * 88)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Der Fall: ein Kraftwerkspark mit 500 MW Zusage\n", + "\n", + "`Chance_Constraints.py`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python3\n", + "\n", + "# Chance_Constraints.py\n", + "\"\"\"\n", + "Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen.\n", + "\n", + "Das Management fragt selten \"was ist im Mittel am besten?\", sondern \"mit\n", + "welcher Sicherheit haelt der Plan?\". Genau das formuliert eine Chance\n", + "Constraint: P(Versorgung >= Bedarf) >= 1 - alpha.\n", + "\n", + "Gezeigt werden beide Wege dorthin, an derselben Instanz:\n", + " (a) analytisch - unter Normalverteilungsannahme wird daraus eine\n", + " Second-Order-Cone-Bedingung, loesbar mit CVXPY\n", + " (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige\n", + " empirische Verteilungen\n", + "\n", + "Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt\n", + "Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung\n", + "nicht normal ist?\n", + "\n", + "Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools\n", + "noch ein direkter highspy-Import, damit alles in einem Prozess laeuft.\n", + "\"\"\"\n", + "\n", + "import cvxpy as cp\n", + "import numpy as np\n", + "from scipy.stats import norm\n", + "\n", + "# --- Der Kraftwerkspark ----------------------------------------------------\n", + "# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel\n", + "# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und\n", + "# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche,\n", + "# Genehmigung, Brennstoffversorgung.\n", + "TECHNIK = [\"Gaskraftwerk\", \"Windpark\", \"Solarpark\", \"Biomasse\"]\n", + "KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr\n", + "VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85])\n", + "STREUUNG = np.array([0.05, 0.16, 0.10, 0.04])\n", + "GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW\n", + "\n", + "# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und\n", + "# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel\n", + "# und nicht zu einer Summe unabhaengiger Einzelzuschlaege.\n", + "KORRELATION = np.array([\n", + " [1.00, 0.00, 0.00, 0.05],\n", + " [0.00, 1.00, -0.25, 0.00],\n", + " [0.00, -0.25, 1.00, 0.00],\n", + " [0.05, 0.00, 0.00, 1.00],\n", + "])\n", + "SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG)\n", + "WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA\n", + "\n", + "BEDARF = 500.0 # MW, die gesichert bereitstehen muessen\n", + "N = len(TECHNIK)\n", + "\n", + "# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich.\n", + "# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das\n", + "# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt.\n", + "# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken.\n", + "P_KAELTEWELLE = 0.08\n", + "EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88])\n", + "\n", + "\n", + "def mittelwertplan():\n", + " \"\"\"Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.\"\"\"\n", + " x = cp.Variable(N, nonneg=True)\n", + " problem = cp.Problem(cp.Minimize(KOSTEN @ x),\n", + " [VERFUEGBAR @ x >= BEDARF, x <= GRENZE])\n", + " problem.solve(solver=cp.CLARABEL)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def chance_constraint_analytisch(alpha):\n", + " \"\"\"\n", + " P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA).\n", + "\n", + " Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF\n", + " mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der\n", + " Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage.\n", + " Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken\n", + " gemeinsam weniger als jede fuer sich.\n", + " \"\"\"\n", + " x = cp.Variable(N, nonneg=True)\n", + " z = norm.ppf(1 - alpha)\n", + " bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF\n", + " problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE])\n", + " problem.solve(solver=cp.CLARABEL)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def chance_constraint_szenarien(a, alpha):\n", + " \"\"\"\n", + " Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine\n", + " Binaervariable z_s, ob es verletzt werden darf.\n", + "\n", + " a_s^T x >= BEDARF - M * z_s fuer alle s\n", + " sum_s z_s <= alpha * S\n", + "\n", + " M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0:\n", + " Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M\n", + " wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die\n", + " Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung.\n", + " \"\"\"\n", + " S = len(a)\n", + " x = cp.Variable(N, nonneg=True)\n", + " z = cp.Variable(S, boolean=True)\n", + " problem = cp.Problem(\n", + " cp.Minimize(KOSTEN @ x),\n", + " [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE])\n", + " problem.solve(solver=cp.SCIPY)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def ziehe_wetter(anzahl, seed):\n", + " \"\"\"Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.\"\"\"\n", + " rng = np.random.default_rng(seed)\n", + " a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl)\n", + " getroffen = rng.random(anzahl) < P_KAELTEWELLE\n", + " a[getroffen] *= EINBRUCH\n", + " return np.clip(a, 0.0, 1.0)\n", + "\n", + "\n", + "def sicherheit(x, a):\n", + " \"\"\"Anteil der Szenarien, in denen der Plan den Bedarf deckt.\"\"\"\n", + " return float(np.mean(a @ x >= BEDARF))\n", + "\n", + "\n", + "KOPF = (f\"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} \"\n", + " f\"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}\")\n", + "\n", + "\n", + "def zeile(name, kosten, x, in_normal, in_echt):\n", + " mix = \" \".join(f\"{w:4.0f}\" for w in x)\n", + " return (f\"{name:<20} {kosten:>13,.0f} {mix} \"\n", + " f\"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %\")\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " print(\"=\" * 78)\n", + " print(\" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK\")\n", + " print(\"=\" * 78)\n", + " print(f\"Gesichert bereitzustellen: {BEDARF:.0f} MW\\n\")\n", + " print(f\"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} \"\n", + " f\"{'Grenze':>8} {'EUR je erw. MW':>15}\")\n", + " print(\"-\" * 78)\n", + " for i, name in enumerate(TECHNIK):\n", + " print(f\"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} \"\n", + " f\"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} \"\n", + " f\"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}\")\n", + " print(f\"\\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, \"\n", + " f\"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.\")\n", + "\n", + " # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen -\n", + " # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung.\n", + " echt = ziehe_wetter(200_000, seed=771)\n", + " normalwelt = np.random.default_rng(4711).multivariate_normal(\n", + " VERFUEGBAR, SIGMA, size=200_000)\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (1) Mittelwertplan und Chance Constraints im Vergleich\")\n", + " print(\"=\" * 78)\n", + " print(KOPF)\n", + " print(\"-\" * 78)\n", + "\n", + " k_mittel, x_mittel = mittelwertplan()\n", + " stufen = [(\"Mittelwert\", k_mittel, sicherheit(x_mittel, normalwelt))]\n", + " print(zeile(\"Mittelwert\", k_mittel, x_mittel,\n", + " sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt)))\n", + "\n", + " plaene = {}\n", + " for alpha in (0.20, 0.10, 0.05, 0.01):\n", + " kosten, x = chance_constraint_analytisch(alpha)\n", + " plaene[alpha] = (kosten, x)\n", + " quote_normal = sicherheit(x, normalwelt)\n", + " stufen.append((f\"Zusage {(1 - alpha) * 100:.0f} %\", kosten, quote_normal))\n", + " print(zeile(f\"Zusage {(1 - alpha) * 100:.0f} %\", kosten, x,\n", + " quote_normal, sicherheit(x, echt)))\n", + "\n", + " print(\"\\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren\")\n", + " print(\" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).\")\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?\")\n", + " print(\"=\" * 78)\n", + " print(f\"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} \"\n", + " f\"{'EUR je Punkt':>13}\")\n", + " print(\"-\" * 78)\n", + " for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]):\n", + " d_punkte = (q2 - q1) * 100\n", + " d_kosten = k2 - k1\n", + " print(f\"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} \"\n", + " f\"{d_kosten / d_punkte:>13,.0f}\")\n", + " erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100)\n", + " letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100)\n", + " print(f\"\\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.\")\n", + " print(\" Sicherheit ist konvex bepreist - genau deshalb muss jemand\")\n", + " print(\" entscheiden, wie viel davon das Unternehmen kaufen will.\")\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (3) Und wenn die Verteilung nicht normal ist?\")\n", + " print(\"=\" * 78)\n", + " k_soc, x_soc = plaene[0.05]\n", + " print(f\"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in \"\n", + " f\"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern\")\n", + " print(f\"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % \"\n", + " f\"und - entscheidend - auch das Gaskraftwerk nur \"\n", + " f\"{EINBRUCH[0] * 100:.0f} %\")\n", + " print(\"des Ueblichen. Alles bricht gleichzeitig ein.\\n\")\n", + "\n", + " a_bau = ziehe_wetter(400, seed=20260908)\n", + " k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05)\n", + "\n", + " print(KOPF)\n", + " print(\"-\" * 78)\n", + " print(zeile(\"SOC, Zusage 95 %\", k_soc, x_soc,\n", + " sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt)))\n", + " print(zeile(\"Szenarien, 95 %\", k_sz, x_sz,\n", + " sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt)))\n", + "\n", + " print(f\"\\n Der SOC-Plan verspricht 95 % und haelt \"\n", + " f\"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist\")\n", + " print(f\" falsch, sondern die Annahme: In der Normalwelt liefert derselbe\")\n", + " print(f\" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.\")\n", + " print(f\"\\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} \"\n", + " f\"Binaervariablen) kommt auf\")\n", + " print(f\" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer \"\n", + " f\"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:\")\n", + " print(\" Was im Ernstfall ausfaellt, hilft der Zusage nicht.\")\n", + " print(\"=\" * 78)" + ] } ], "metadata": { diff --git a/OR_HTML_04/Notebooks_04/unsicherheit.ipynb b/OR_HTML_04/Notebooks_04/unsicherheit.ipynb index 2d53647..82b5421 100644 --- a/OR_HTML_04/Notebooks_04/unsicherheit.ipynb +++ b/OR_HTML_04/Notebooks_04/unsicherheit.ipynb @@ -451,6 +451,251 @@ " print(\" als sie im Normalfall kostet.\")\n", " print(\"=\" * 88)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Der Fall: ein Kraftwerkspark mit 500 MW Zusage\n", + "\n", + "`Chance_Constraints.py`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python3\n", + "\n", + "# Chance_Constraints.py\n", + "\"\"\"\n", + "Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen.\n", + "\n", + "Das Management fragt selten \"was ist im Mittel am besten?\", sondern \"mit\n", + "welcher Sicherheit haelt der Plan?\". Genau das formuliert eine Chance\n", + "Constraint: P(Versorgung >= Bedarf) >= 1 - alpha.\n", + "\n", + "Gezeigt werden beide Wege dorthin, an derselben Instanz:\n", + " (a) analytisch - unter Normalverteilungsannahme wird daraus eine\n", + " Second-Order-Cone-Bedingung, loesbar mit CVXPY\n", + " (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige\n", + " empirische Verteilungen\n", + "\n", + "Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt\n", + "Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung\n", + "nicht normal ist?\n", + "\n", + "Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools\n", + "noch ein direkter highspy-Import, damit alles in einem Prozess laeuft.\n", + "\"\"\"\n", + "\n", + "import cvxpy as cp\n", + "import numpy as np\n", + "from scipy.stats import norm\n", + "\n", + "# --- Der Kraftwerkspark ----------------------------------------------------\n", + "# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel\n", + "# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und\n", + "# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche,\n", + "# Genehmigung, Brennstoffversorgung.\n", + "TECHNIK = [\"Gaskraftwerk\", \"Windpark\", \"Solarpark\", \"Biomasse\"]\n", + "KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr\n", + "VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85])\n", + "STREUUNG = np.array([0.05, 0.16, 0.10, 0.04])\n", + "GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW\n", + "\n", + "# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und\n", + "# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel\n", + "# und nicht zu einer Summe unabhaengiger Einzelzuschlaege.\n", + "KORRELATION = np.array([\n", + " [1.00, 0.00, 0.00, 0.05],\n", + " [0.00, 1.00, -0.25, 0.00],\n", + " [0.00, -0.25, 1.00, 0.00],\n", + " [0.05, 0.00, 0.00, 1.00],\n", + "])\n", + "SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG)\n", + "WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA\n", + "\n", + "BEDARF = 500.0 # MW, die gesichert bereitstehen muessen\n", + "N = len(TECHNIK)\n", + "\n", + "# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich.\n", + "# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das\n", + "# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt.\n", + "# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken.\n", + "P_KAELTEWELLE = 0.08\n", + "EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88])\n", + "\n", + "\n", + "def mittelwertplan():\n", + " \"\"\"Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.\"\"\"\n", + " x = cp.Variable(N, nonneg=True)\n", + " problem = cp.Problem(cp.Minimize(KOSTEN @ x),\n", + " [VERFUEGBAR @ x >= BEDARF, x <= GRENZE])\n", + " problem.solve(solver=cp.CLARABEL)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def chance_constraint_analytisch(alpha):\n", + " \"\"\"\n", + " P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA).\n", + "\n", + " Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF\n", + " mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der\n", + " Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage.\n", + " Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken\n", + " gemeinsam weniger als jede fuer sich.\n", + " \"\"\"\n", + " x = cp.Variable(N, nonneg=True)\n", + " z = norm.ppf(1 - alpha)\n", + " bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF\n", + " problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE])\n", + " problem.solve(solver=cp.CLARABEL)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def chance_constraint_szenarien(a, alpha):\n", + " \"\"\"\n", + " Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine\n", + " Binaervariable z_s, ob es verletzt werden darf.\n", + "\n", + " a_s^T x >= BEDARF - M * z_s fuer alle s\n", + " sum_s z_s <= alpha * S\n", + "\n", + " M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0:\n", + " Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M\n", + " wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die\n", + " Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung.\n", + " \"\"\"\n", + " S = len(a)\n", + " x = cp.Variable(N, nonneg=True)\n", + " z = cp.Variable(S, boolean=True)\n", + " problem = cp.Problem(\n", + " cp.Minimize(KOSTEN @ x),\n", + " [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE])\n", + " problem.solve(solver=cp.SCIPY)\n", + " return problem.value, x.value\n", + "\n", + "\n", + "def ziehe_wetter(anzahl, seed):\n", + " \"\"\"Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.\"\"\"\n", + " rng = np.random.default_rng(seed)\n", + " a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl)\n", + " getroffen = rng.random(anzahl) < P_KAELTEWELLE\n", + " a[getroffen] *= EINBRUCH\n", + " return np.clip(a, 0.0, 1.0)\n", + "\n", + "\n", + "def sicherheit(x, a):\n", + " \"\"\"Anteil der Szenarien, in denen der Plan den Bedarf deckt.\"\"\"\n", + " return float(np.mean(a @ x >= BEDARF))\n", + "\n", + "\n", + "KOPF = (f\"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} \"\n", + " f\"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}\")\n", + "\n", + "\n", + "def zeile(name, kosten, x, in_normal, in_echt):\n", + " mix = \" \".join(f\"{w:4.0f}\" for w in x)\n", + " return (f\"{name:<20} {kosten:>13,.0f} {mix} \"\n", + " f\"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %\")\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " print(\"=\" * 78)\n", + " print(\" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK\")\n", + " print(\"=\" * 78)\n", + " print(f\"Gesichert bereitzustellen: {BEDARF:.0f} MW\\n\")\n", + " print(f\"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} \"\n", + " f\"{'Grenze':>8} {'EUR je erw. MW':>15}\")\n", + " print(\"-\" * 78)\n", + " for i, name in enumerate(TECHNIK):\n", + " print(f\"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} \"\n", + " f\"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} \"\n", + " f\"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}\")\n", + " print(f\"\\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, \"\n", + " f\"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.\")\n", + "\n", + " # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen -\n", + " # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung.\n", + " echt = ziehe_wetter(200_000, seed=771)\n", + " normalwelt = np.random.default_rng(4711).multivariate_normal(\n", + " VERFUEGBAR, SIGMA, size=200_000)\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (1) Mittelwertplan und Chance Constraints im Vergleich\")\n", + " print(\"=\" * 78)\n", + " print(KOPF)\n", + " print(\"-\" * 78)\n", + "\n", + " k_mittel, x_mittel = mittelwertplan()\n", + " stufen = [(\"Mittelwert\", k_mittel, sicherheit(x_mittel, normalwelt))]\n", + " print(zeile(\"Mittelwert\", k_mittel, x_mittel,\n", + " sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt)))\n", + "\n", + " plaene = {}\n", + " for alpha in (0.20, 0.10, 0.05, 0.01):\n", + " kosten, x = chance_constraint_analytisch(alpha)\n", + " plaene[alpha] = (kosten, x)\n", + " quote_normal = sicherheit(x, normalwelt)\n", + " stufen.append((f\"Zusage {(1 - alpha) * 100:.0f} %\", kosten, quote_normal))\n", + " print(zeile(f\"Zusage {(1 - alpha) * 100:.0f} %\", kosten, x,\n", + " quote_normal, sicherheit(x, echt)))\n", + "\n", + " print(\"\\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren\")\n", + " print(\" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).\")\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?\")\n", + " print(\"=\" * 78)\n", + " print(f\"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} \"\n", + " f\"{'EUR je Punkt':>13}\")\n", + " print(\"-\" * 78)\n", + " for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]):\n", + " d_punkte = (q2 - q1) * 100\n", + " d_kosten = k2 - k1\n", + " print(f\"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} \"\n", + " f\"{d_kosten / d_punkte:>13,.0f}\")\n", + " erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100)\n", + " letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100)\n", + " print(f\"\\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.\")\n", + " print(\" Sicherheit ist konvex bepreist - genau deshalb muss jemand\")\n", + " print(\" entscheiden, wie viel davon das Unternehmen kaufen will.\")\n", + "\n", + " print(\"\\n\" + \"=\" * 78)\n", + " print(\" (3) Und wenn die Verteilung nicht normal ist?\")\n", + " print(\"=\" * 78)\n", + " k_soc, x_soc = plaene[0.05]\n", + " print(f\"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in \"\n", + " f\"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern\")\n", + " print(f\"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % \"\n", + " f\"und - entscheidend - auch das Gaskraftwerk nur \"\n", + " f\"{EINBRUCH[0] * 100:.0f} %\")\n", + " print(\"des Ueblichen. Alles bricht gleichzeitig ein.\\n\")\n", + "\n", + " a_bau = ziehe_wetter(400, seed=20260908)\n", + " k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05)\n", + "\n", + " print(KOPF)\n", + " print(\"-\" * 78)\n", + " print(zeile(\"SOC, Zusage 95 %\", k_soc, x_soc,\n", + " sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt)))\n", + " print(zeile(\"Szenarien, 95 %\", k_sz, x_sz,\n", + " sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt)))\n", + "\n", + " print(f\"\\n Der SOC-Plan verspricht 95 % und haelt \"\n", + " f\"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist\")\n", + " print(f\" falsch, sondern die Annahme: In der Normalwelt liefert derselbe\")\n", + " print(f\" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.\")\n", + " print(f\"\\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} \"\n", + " f\"Binaervariablen) kommt auf\")\n", + " print(f\" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer \"\n", + " f\"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:\")\n", + " print(\" Was im Ernstfall ausfaellt, hilft der Zusage nicht.\")\n", + " print(\"=\" * 78)" + ] } ], "metadata": { diff --git a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf index 3c608b7..d5e8848 100644 Binary files a/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf and b/OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf differ diff --git a/OR_HTML_04/anhang-loesungen.html b/OR_HTML_04/anhang-loesungen.html index bda4b0b..eb8f287 100644 --- a/OR_HTML_04/anhang-loesungen.html +++ b/OR_HTML_04/anhang-loesungen.html @@ -801,11 +801,12 @@

A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“

9.1 — Fluch des Durchschnitts. Weil sich Verzögerungen fortpflanzen und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (der nächste Schritt ist noch nicht bereit), ein verspätetes verzögert alles. Weitere Beispiele: Wartezeiten in Warteschlangen (nichtlinear in der Auslastung), Projektkosten mit Nachträgen.

-

9.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor).

+

9.2 — Ansatz wählen. (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage).

9.3 — Zweistufig rechnen. (a) Mit Spot = 60: Ableitung bei x \in (100,250): 40 - 60\cdot0{,}5 + 5\cdot0{,}5 = 40-30+2{,}5 = +12{,}5 > 0 → Kapazität senken. Bei x < 100: 40 - 60 = -20 < 0 → erhöhen. Optimum daher x^* = 100. (b) Billige Nachbesserung macht Vorhalten unattraktiv. (c) Bei x = 225 optimal müsste die Ableitung dort das Vorzeichen wechseln — das ist bei diskreten Szenarien nur zufällig der Fall; der Mittelwert ist nur bei symmetrischen Kosten und stetiger Verteilung optimal.

9.4 — EVPI interpretieren. Selbst eine perfekte Prognose wäre nur 8 400 € pro Jahr wert. Eine Lösung für 15 000 € kann sich also niemals rechnen — unabhängig von ihrer Güte. Argument: „Der theoretische Maximalnutzen liegt unter dem Preis.“

9.5 — Monte-Carlo erweitern. (a) Servicelevel 95 %: Kapazität = 95 %-Quantil des Bedarfs (ca. 485). Die Zusatzkosten gegenüber dem Kostenoptimum (180) betragen ein Vielfaches — Servicelevel ist teuer, und genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (b) cvar = kosten[kosten >= np.quantile(kosten, 0.95)].mean(). (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben.

-

9.6 — Budgeted Uncertainty.

+

9.6 — Den Preis der Zusage selbst bestimmen. (a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die Norm, tragen zur gesicherten Leistung aber kaum bei. (b) Der Unterschied ist nicht numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert selbst der Vollausbau in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese Fälle in den Daten und meldet korrekt infeasible, sobald die Zusage über 92 % steigt. Das analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet optimal — und der Plan hält gemessen 87,4 %. Glauben sollte man dem Szenariomodell: Ein infeasible ist hier die richtige Antwort. Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. (c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“

+

9.7 — Budgeted Uncertainty.

abzug = cp.sum_largest(cp.multiply(UNSICHERHEIT, w), Gamma)
 ziel = cp.Maximize(MU_SCHAETZUNG @ w - abzug - 0.5*LAMBDA*cp.quad_form(w, SIGMA))

\Gamma = 0 entspricht dem nominalen Fall, \Gamma = n dem vollen Worst Case. Dazwischen steuert \Gamma die Vorsicht stufenlos — der praktisch nützlichste Bereich liegt meist bei \Gamma \approx \sqrt{n}.

@@ -852,6 +853,8 @@ F(11{,}33) = 1 - \frac{(18-11{,}33)^2}{(18-6)(18-10)} = 1 - \frac{44{,}4}{96} =
  • Die Differenz zwischen Kosten unter Unsicherheit und Kosten bei perfektem Wissen — eine Obergrenze für den Wert jeder Prognoseverbesserung.
  • Weil sie nur eine Menge möglicher Werte braucht, nicht deren Verteilung — sie optimiert gegen das schlechteste Element dieser Menge.
  • Für einfache Mengen (Box, Ellipsoid) lässt sich das innere Maximum geschlossen ausrechnen und wird zu einem Abzugsterm; das Gesamtproblem bleibt konvex.
  • +
  • Weil die Streuung der Summe \mathbf{a}^\top\mathbf{x} nicht die Summe der Streuungen ist: \sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}} enthält die Kovarianzen. Ein fester Zuschlag je Variable wäre linear und würde deshalb übersehen, dass sich gegenläufige Größen teilweise aufheben — die Norm belohnt Mischung, ein linearer Aufschlag nicht.
  • +
  • Weil die Zusage nur so gut ist wie die unterstellte Verteilung. Die Normalverteilung kennt keine Ereignisse, in denen alle Quellen gleichzeitig ausfallen; ihre schwachen Korrelationen unterschätzen genau den Fall, der die Zusage bricht. Die szenariobasierte Formulierung hat diese Fälle in den Daten und trifft die Quote — erkauft mit Mehrkosten und mit dem Risiko, sich an die verwendete Stichprobe anzupassen.

  • A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“

    diff --git a/OR_HTML_04/assets/programme.js b/OR_HTML_04/assets/programme.js index 0bb6ed2..5c39e70 100644 --- a/OR_HTML_04/assets/programme.js +++ b/OR_HTML_04/assets/programme.js @@ -1 +1 @@ -window.OR_PROGRAMME = ["Backtest_Fallen.py", "Bausteine_Vorlage.py", "Bellman_Minimalbeispiel.py", "Benchmark_Skalierung.py", "Betriebsueberwachung.py", "Big_M_Falle.py", "Bot_Allokation.py", "Brute_Force_Vergleich.py", "CP_SAT_Statusfaelle.py", "CP_SAT_Vertretungssystem.py", "CVaR_Portfolio.py", "Data_Snooping.py", "Diversifikation_Demo.py", "Dualitaet_Nachweis.py", "Ein_System_Vier_Ansaetze.py", "Entropie_Maximierte_Allokation.py", "Erklaerbarkeit.py", "Excel_Bruecke.py", "Finanzdaten_Ledoit_Wolf.py", "Fluch_des_Durchschnitts.py", "Infeasibility_Diagnose.py", "Installationstest.py", "JobShop_Intervalle.py", "KKT_Nachweis.py", "Konfliktsuche.py", "Konvexitaet_Demo.py", "Kovarianz_Falle.py", "Kraftwerkseinsatz.py", "Large_Neighborhood_Search.py", "Lokale_Optima_Multistart.py", "MILP_Portfolio_Fixgebuehren.py", "Markowitz_CVXPY.py", "Matrixform.py", "Mehrperiodige_Order_Execution.py", "Mehrziel_Pareto.py", "Metaheuristik_vs_Exakt.py", "Min_Cost_Flow.py", "Modellierungsschichten.py", "Monte_Carlo.py", "Mutationstest.py", "Optimierungsdienst.py", "Predict_then_Optimize.py", "Propagation_Demo.py", "QP_Grundlagen.py", "QuantitativeTradingEngine.py", "Renditen_Vergleich.py", "Renditeschaetzung_Falle.py", "Robuste_Optimierung.py", "Rucksack.py", "Runden_Gegenbeispiel.py", "Schaetzrauschen_Demo.py", "Sensitivitaetsanalyse.py", "Simplex_Tableau_LP.py", "Simulated_Annealing.py", "Skalierung_Kondition.py", "Solver_Wahl.py", "Solverstatus_und_Gap.py", "Solverwechsel_CPSAT_HiGHS.py", "Spaltengenerierung.py", "Stochastische_Optimierung.py", "Strafgewichte.py", "Strukturbruecke.py", "Toleranzen_und_Entartung.py", "VRP_Flotten_Routing.py", "VRP_Kapazitaetsfalle.py", "VaR_CVaR_Demo.py", "Vektorisierte_Modellgenerierung.py", "Visualisierung_Loesungsraum.py", "Vom_Wunsch_zum_Modell.py", "Warmstart_Effekt.py", "Zuordnung_Ungarisch.py", "or_kern.py", "test_or_kern.py"]; +window.OR_PROGRAMME = ["Backtest_Fallen.py", "Bausteine_Vorlage.py", "Bellman_Minimalbeispiel.py", "Benchmark_Skalierung.py", "Betriebsueberwachung.py", "Big_M_Falle.py", "Bot_Allokation.py", "Brute_Force_Vergleich.py", "CP_SAT_Statusfaelle.py", "CP_SAT_Vertretungssystem.py", "CVaR_Portfolio.py", "Chance_Constraints.py", "Data_Snooping.py", "Diversifikation_Demo.py", "Dualitaet_Nachweis.py", "Ein_System_Vier_Ansaetze.py", "Entropie_Maximierte_Allokation.py", "Erklaerbarkeit.py", "Excel_Bruecke.py", "Finanzdaten_Ledoit_Wolf.py", "Fluch_des_Durchschnitts.py", "Infeasibility_Diagnose.py", "Installationstest.py", "JobShop_Intervalle.py", "KKT_Nachweis.py", "Konfliktsuche.py", "Konvexitaet_Demo.py", "Kovarianz_Falle.py", "Kraftwerkseinsatz.py", "Large_Neighborhood_Search.py", "Lokale_Optima_Multistart.py", "MILP_Portfolio_Fixgebuehren.py", "Markowitz_CVXPY.py", "Matrixform.py", "Mehrperiodige_Order_Execution.py", "Mehrziel_Pareto.py", "Metaheuristik_vs_Exakt.py", "Min_Cost_Flow.py", "Modellierungsschichten.py", "Monte_Carlo.py", "Mutationstest.py", "Optimierungsdienst.py", "Predict_then_Optimize.py", "Propagation_Demo.py", "QP_Grundlagen.py", "QuantitativeTradingEngine.py", "Renditen_Vergleich.py", "Renditeschaetzung_Falle.py", "Robuste_Optimierung.py", "Rucksack.py", "Runden_Gegenbeispiel.py", "Schaetzrauschen_Demo.py", "Sensitivitaetsanalyse.py", "Simplex_Tableau_LP.py", "Simulated_Annealing.py", "Skalierung_Kondition.py", "Solver_Wahl.py", "Solverstatus_und_Gap.py", "Solverwechsel_CPSAT_HiGHS.py", "Spaltengenerierung.py", "Stochastische_Optimierung.py", "Strafgewichte.py", "Strukturbruecke.py", "Toleranzen_und_Entartung.py", "VRP_Flotten_Routing.py", "VRP_Kapazitaetsfalle.py", "VaR_CVaR_Demo.py", "Vektorisierte_Modellgenerierung.py", "Visualisierung_Loesungsraum.py", "Vom_Wunsch_zum_Modell.py", "Warmstart_Effekt.py", "Zuordnung_Ungarisch.py", "or_kern.py", "test_or_kern.py"]; diff --git a/OR_HTML_04/assets/search-index.js b/OR_HTML_04/assets/search-index.js index d2bd3d0..8325380 100644 --- a/OR_HTML_04/assets/search-index.js +++ b/OR_HTML_04/assets/search-index.js @@ -1 +1 @@ -window.OR_SEARCH_INDEX = [{"seite":"vorwort.html","titel":"Vorwort & Lesehilfe","anker":null,"text":""},{"seite":"notation.html","titel":"Notation & Abkürzungen","anker":null,"text":""},{"seite":"notation.html","titel":"1. Die Symbole der Optimierung","anker":"die-symbole-der-optimierung","text":""},{"seite":"notation.html","titel":"1.1 Grundgrößen jedes Modells","anker":"grundgrößen-jedes-modells","text":""},{"seite":"notation.html","titel":"1.2 Mengen und Zahlbereiche","anker":"mengen-und-zahlbereiche","text":""},{"seite":"notation.html","titel":"1.3 Operatoren und Schreibweisen","anker":"operatoren-und-schreibweisen","text":""},{"seite":"notation.html","titel":"1.4 Symbole der Finanzkapitel (Teil IV)","anker":"symbole-der-finanzkapitel-teil-iv","text":""},{"seite":"notation.html","titel":"1.5 Symbole der Verfahrenskapitel (Teil II und III)","anker":"symbole-der-verfahrenskapitel-teil-ii-und-iii","text":"Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinausgehen."},{"seite":"notation.html","titel":"2. Abkürzungen — vollständig ausgeschrieben","anker":"abkürzungen-vollständig-ausgeschrieben","text":""},{"seite":"notation.html","titel":"2.1 Problemklassen und Verfahren","anker":"problemklassen-und-verfahren","text":""},{"seite":"notation.html","titel":"2.2 Probleme mit Eigennamen","anker":"probleme-mit-eigennamen","text":""},{"seite":"notation.html","titel":"2.3 Finanz- und Risikokennzahlen","anker":"finanz--und-risikokennzahlen","text":""},{"seite":"notation.html","titel":"2.4 Software, Solver und Schnittstellen","anker":"software-solver-und-schnittstellen","text":""},{"seite":"notation.html","titel":"2.5 Begriffe aus dem Solver-Alltag","anker":"begriffe-aus-dem-solver-alltag","text":""},{"seite":"notation.html","titel":"3. Konventionen in diesem Buch","anker":"konventionen-in-diesem-buch","text":"Vektoren sind grundsätzlich Spaltenvektoren und werden fett gesetzt: \\mathbf{x}. Ein transponierter Vektor \\mathbf{x}^\\top ist eine Zeile. Skalare bleiben mager: n, Z."},{"seite":"einfuehrung.html","titel":"Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung","anker":null,"text":""},{"seite":"einfuehrung.html","titel":"1.1 In 5 Minuten gelöst","anker":"sec:einfuehrung-schnellstart","text":"Bevor wir über Theorie sprechen, lösen Sie ein echtes Problem. Kopieren Sie den folgenden Block, führen Sie ihn aus, sehen Sie das Ergebnis — die Erklärung kommt danach."},{"seite":"einfuehrung.html","titel":"1.2 Lernziele","anker":"sec:einfuehrung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"einfuehrung.html","titel":"1.3 Was ist Operations Research wirklich?","anker":"sec:einfuehrung-was-ist-operations-research-wirklich","text":"Im Alltag lösen wir Probleme intuitiv: durch Ausprobieren, Daumenregeln, Erfahrung. Das funktioniert erstaunlich gut — bis eine bestimmte Schwelle überschritten wird. Dann bricht d"},{"seite":"einfuehrung.html","titel":"Die drei Stufen der Analytik","anker":"die-drei-stufen-der-analytik","text":"OR beantwortet nicht die beschreibende Frage „Was ist passiert?“ und nicht die vorhersagende Frage „Was wird passieren?“, sondern die vorschreibende (präskriptive) Kernfrage:"},{"seite":"einfuehrung.html","titel":"1.4 Warum Ausprobieren scheitert — mit eigener Rechnung","anker":"sec:einfuehrung-warum-ausprobieren-scheitert-mit-eigener-rechnung","text":"Die Behauptung „vollständiges Durchprobieren ist unmöglich“ wird oft aufgestellt und selten belegt. Rechnen wir sie einmal selbst nach — das Ergebnis prägt sich besser ein als jede"},{"seite":"einfuehrung.html","titel":"1.5 Historischer Kontext und Evolution","anker":"sec:einfuehrung-historischer-kontext-und-evolution","text":"Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man"},{"seite":"einfuehrung.html","titel":"1.6 Die vier universellen Bausteine jedes OR-Problems","anker":"sec:einfuehrung-die-vier-universellen-bausteine-jedes-or","text":"Dies ist der wichtigste Abschnitt des Kapitels. Jedes Optimierungsproblem der Welt lässt sich — unabhängig von der Branche — auf vier Elemente reduzieren. Wenn Sie später vor einem"},{"seite":"einfuehrung.html","titel":"Baustein 1 — Entscheidungsvariablen (x)","anker":"baustein-1-entscheidungsvariablen-x","text":"Die Stellschrauben des Modells: Werte, die der Algorithmus aktiv festlegen darf."},{"seite":"einfuehrung.html","titel":"Baustein 2 — Parameter / Eingabedaten (c, A, b)","anker":"baustein-2-parameter-eingabedaten-c-a-b","text":"Die unveränderlichen Fakten der Realität: historische Renditen, Maschinenstundensätze, Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen."},{"seite":"einfuehrung.html","titel":"Baustein 3 — Zielfunktion (f(x))","anker":"baustein-3-zielfunktion-fx","text":"Ein mathematischer Ausdruck, der die Güte einer Lösung zu einer einzigen Zahl verdichtet:"},{"seite":"einfuehrung.html","titel":"Baustein 4 — Nebenbedingungen (Constraints)","anker":"baustein-4-nebenbedingungen-constraints","text":"Regeln, die den Raum der erlaubten Lösungen einschränken:"},{"seite":"einfuehrung.html","titel":"Die vier Bausteine als eine Formel","anker":"die-vier-bausteine-als-eine-formel","text":"Setzt man die vier Bausteine zusammen, entsteht die Standardform, in der Ihnen jedes Optimierungsmodell dieses Buches begegnen wird. Sie sieht auf den ersten Blick sperrig aus — de"},{"seite":"einfuehrung.html","titel":"Die Bausteine in der Praxis: eine Vorlage","anker":"die-bausteine-in-der-praxis-eine-vorlage","text":"Damit die Zerlegung zur Gewohnheit wird, hier eine Vorlage, die Sie bei jedem neuen Problem ausfüllen können — vor der ersten Codezeile."},{"seite":"einfuehrung.html","titel":"1.7 Erstes Python-Vollbeispiel: Ressourcenallokation im Rechenzentrum","anker":"sec:einfuehrung-erstes-python-vollbeispiel-ressourcenallokation-im-rechenzentrum","text":""},{"seite":"einfuehrung.html","titel":"Szenario","anker":"szenario","text":"Ein quantitatives Handelshaus betreibt Serverkapazitäten für zwei Algorithmentypen:"},{"seite":"einfuehrung.html","titel":"Mathematische Formulierung","anker":"mathematische-formulierung","text":"Entscheidungsvariablen:"},{"seite":"einfuehrung.html","titel":"Umsetzung mit Google OR-Tools","anker":"umsetzung-mit-google-or-tools","text":""},{"seite":"einfuehrung.html","titel":"1.8 Von Excel zu Python: Ihre Daten liegen schon da","anker":"sec:einfuehrung-excel-bruecke","text":"In den allermeisten Betrieben liegen die Zahlen, die ein Optimierungsmodell braucht, nicht in einer Datenbank, sondern in einer Tabellenkalkulation. Und viele Leserinnen und Leser "},{"seite":"einfuehrung.html","titel":"1.9 Übungsaufgaben","anker":"sec:einfuehrung-uebungsaufgaben","text":""},{"seite":"einfuehrung.html","titel":"1.10 Finde den Denkfehler","anker":"sec:einfuehrung-denkfehler","text":"Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht stimmt. Gefährlich sind Programme, die fehlerfrei durchlaufen, einen Status OPTIMAL melden und eine Zah"},{"seite":"einfuehrung.html","titel":"1.11 Micro-Quiz","anker":"sec:einfuehrung-quiz","text":""},{"seite":"einfuehrung.html","titel":"1.12 Selbsttest","anker":"sec:einfuehrung-selbsttest","text":""},{"seite":"einfuehrung.html","titel":"1.13 Zusammenfassung","anker":"sec:einfuehrung-zusammenfassung","text":""},{"seite":"fundament.html","titel":"Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität","anker":null,"text":""},{"seite":"fundament.html","titel":"2.1 In 5 Minuten gelöst","anker":"sec:fundament-schnellstart","text":"Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy."},{"seite":"fundament.html","titel":"2.2 Lernziele","anker":"sec:fundament-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"fundament.html","titel":"2.3 Warum überhaupt Vektoren und Matrizen?","anker":"sec:fundament-warum-ueberhaupt-vektoren-und-matrizen","text":"In Kapitel 1 hatten wir zwei Variablen und drei Nebenbedingungen. Das ließ sich bequem ausschreiben. Reale Modelle haben hunderte bis hunderttausende Variablen — dort wäre Ausschre"},{"seite":"fundament.html","titel":"Die Bausteine","anker":"die-bausteine","text":"Sei n die Anzahl der Entscheidungsvariablen und m die Anzahl der Nebenbedingungen."},{"seite":"fundament.html","titel":"Die kanonische Standardform","anker":"die-kanonische-standardform","text":"Damit lässt sich jedes lineare Programm in drei Zeilen schreiben:"},{"seite":"fundament.html","titel":"Dasselbe in Python","anker":"dasselbe-in-python","text":""},{"seite":"fundament.html","titel":"2.4 Der zulässige Lösungsraum und das Polyeder","anker":"sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder","text":"Jede lineare Ungleichung a_{i1}x_1 + a_{i2}x_2 \\le b_i definiert geometrisch eine Halbebene (im \\mathbb{R}^2) bzw. einen Halbraum (im \\mathbb{R}^n): Die zugehörige Gleichung ist ei"},{"seite":"fundament.html","titel":"Der Fundamentalsatz der linearen Optimierung","anker":"der-fundamentalsatz-der-linearen-optimierung","text":""},{"seite":"fundament.html","titel":"2.5 Konvexität: die Grenze zwischen leicht und schwer","anker":"sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und","text":"Hier kommt die vielleicht wichtigste Einsicht des ganzen Buches. In der Optimierungstheorie verläuft die Trennlinie zwischen „zuverlässig lösbar“ und „im Allgemeinen hoffnungslos“ "},{"seite":"fundament.html","titel":"Konvexe Menge","anker":"konvexe-menge","text":"Eine Menge \\mathcal{C} \\subseteq \\mathbb{R}^n heißt konvex, wenn für alle Punkte \\mathbf{x}, \\mathbf{y} \\in \\mathcal{C} und jedes \\theta \\in [0,1] gilt:"},{"seite":"fundament.html","titel":"Konvexe Funktion","anker":"konvexe-funktion","text":"Eine Funktion f: \\mathcal{C} \\to \\mathbb{R} heißt konvex, wenn für alle \\mathbf{x}, \\mathbf{y} \\in \\mathcal{C} und \\theta \\in [0,1] gilt:"},{"seite":"fundament.html","titel":"Der zentrale Satz","anker":"der-zentrale-satz","text":""},{"seite":"fundament.html","titel":"Was ist konvex, was nicht?","anker":"was-ist-konvex-was-nicht","text":""},{"seite":"fundament.html","titel":"Konvexität sichtbar machen","anker":"konvexität-sichtbar-machen","text":""},{"seite":"fundament.html","titel":"2.6 Geometrische Visualisierung des Lösungsraums","anker":"sec:fundament-geometrische-visualisierung-des-loesungsraums","text":"Wir zeichnen nun den Lösungsraum aus Kapitel 1 und lesen das Optimum ab. Zweidimensionale Bilder sind der schnellste Weg, Intuition für höhere Dimensionen aufzubauen — auch wenn ma"},{"seite":"fundament.html","titel":"2.7 Wenn die Zahlen nicht zusammenpassen: Kondition und Skalierung","anker":"sec:fundament-kondition","text":"Bis hierher war die lineare Algebra exakt. Ein Rechner rechnet aber nicht exakt, sondern mit rund 16 signifikanten Stellen — und Ihre Betriebsdaten sind ohnehin nur auf wenige Stel"},{"seite":"fundament.html","titel":"Die geometrische Vorstellung: zwei fast parallele Linien","anker":"die-geometrische-vorstellung-zwei-fast-parallele-linien","text":"Zwei Nebenbedingungen legen im Zweidimensionalen einen Schnittpunkt fest. Stehen die zugehörigen Geraden kreuzweise aufeinander, ist der Schnittpunkt robust: Verschiebt man eine Ge"},{"seite":"fundament.html","titel":"Woher schlechte Kondition im Alltag kommt","anker":"woher-schlechte-kondition-im-alltag-kommt","text":"Fast nie aus exotischer Mathematik — fast immer aus Einheiten:"},{"seite":"fundament.html","titel":"Ruiz-Equilibrierung: das Modell gesundrechnen","anker":"ruiz-equilibrierung-das-modell-gesundrechnen","text":"Die Gegenmaßnahme ist erstaunlich schlicht. Man multipliziert jede Zeile und jede Spalte der Matrix mit einem Faktor, sodass die Beträge überall in derselben Größenordnung landen:"},{"seite":"fundament.html","titel":"Das Experiment","anker":"das-experiment","text":"Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und schließt mit der praktischen Konsequenz, die Sie in Kapitel 6 wieder brauchen werden: warum e"},{"seite":"fundament.html","titel":"2.8 Übungsaufgaben","anker":"sec:fundament-uebungsaufgaben","text":""},{"seite":"fundament.html","titel":"2.9 Finde den Denkfehler","anker":"sec:fundament-denkfehler","text":""},{"seite":"fundament.html","titel":"2.10 Micro-Quiz","anker":"sec:fundament-quiz","text":""},{"seite":"fundament.html","titel":"2.11 Selbsttest","anker":"sec:fundament-selbsttest","text":""},{"seite":"fundament.html","titel":"2.12 Zusammenfassung","anker":"sec:fundament-zusammenfassung","text":""},{"seite":"oekosystem.html","titel":"Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten","anker":null,"text":""},{"seite":"oekosystem.html","titel":"3.1 In 5 Minuten gelöst","anker":"sec:oekosystem-schnellstart","text":""},{"seite":"oekosystem.html","titel":"3.2 Lernziele","anker":"sec:oekosystem-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"oekosystem.html","titel":"3.3 Die Zwei-Schichten-Architektur","anker":"sec:oekosystem-die-zwei-schichten-architektur","text":"Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbst. Man nutzt eine zweischichtige Architektur:"},{"seite":"oekosystem.html","titel":"3.4 Die Werkzeuge im Vergleich","anker":"sec:oekosystem-die-werkzeuge-im-vergleich","text":""},{"seite":"oekosystem.html","titel":"Die Entscheidung in drei Fragen","anker":"die-entscheidung-in-drei-fragen","text":"Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:"},{"seite":"oekosystem.html","titel":"3.5 Ein System — vier Programmieransätze","anker":"sec:oekosystem-ein-system-vier-programmieransaetze","text":"Nichts macht die Unterschiede so deutlich wie dasselbe Problem, viermal gelöst. Wir nehmen:"},{"seite":"oekosystem.html","titel":"Was das CSR-Format bedeutet","anker":"was-das-csr-format-bedeutet","text":"highspy erwartet die Nebenbedingungsmatrix im CSR-Format (Compressed Sparse Row, komprimierte Zeilendarstellung). Statt der vollen Matrix speichert man nur die Einträge ungleich nu"},{"seite":"oekosystem.html","titel":"3.6 Wann lohnt sich welche Ebene?","anker":"sec:oekosystem-wann-lohnt-sich-welche-ebene","text":""},{"seite":"oekosystem.html","titel":"3.7 Modellierungsschichten für große Modelle: Pyomo und Linopy","anker":"sec:oekosystem-pyomo-linopy","text":"Die vier Bibliotheken aus dem Vierfach-Vergleich decken den Alltag weitgehend ab. Sobald Modelle industrielle Größe erreichen — zehntausende Variablen, Daten aus mehreren Systemen,"},{"seite":"oekosystem.html","titel":"Pyomo: die algebraische Denkweise","anker":"pyomo-die-algebraische-denkweise","text":"Pyomo ist im deutschsprachigen Raum der De-facto-Standard für große LP- und MILP-Modelle in Energiewirtschaft, Chemie und Logistik. Sein Kennzeichen: Es denkt in Mengen und Indizes"},{"seite":"oekosystem.html","titel":"Linopy: eine Zeile, zehntausend Nebenbedingungen","anker":"linopy-eine-zeile-zehntausend-nebenbedingungen","text":"Linopy verfolgt einen anderen Ansatz: Variablen sind beschriftete Arrays (xarray), keine indizierten Einzelobjekte. Damit wird aus"},{"seite":"oekosystem.html","titel":"Die Schichten im Überblick","anker":"die-schichten-im-überblick","text":""},{"seite":"oekosystem.html","titel":"3.8 Wo die Zeit wirklich hingeht: vektorisierte Modellgenerierung","anker":"sec:oekosystem-vektorisierung","text":"Eine der hartnäckigsten Fehlannahmen in Optimierungsprojekten lautet: „Wenn es zu langsam ist, brauchen wir einen besseren Solver.“ Messen Sie erst — oft stimmt das nicht."},{"seite":"oekosystem.html","titel":"Vier Stufen an einem Transportproblem","anker":"vier-stufen-an-einem-transportproblem","text":"Wir bauen dasselbe Transportproblem (m Werke, n Kunden, m \\cdot n Variablen) auf vier Arten auf:"},{"seite":"oekosystem.html","titel":"3.9 Übungsaufgaben","anker":"sec:oekosystem-uebungsaufgaben","text":""},{"seite":"oekosystem.html","titel":"3.10 Finde den Denkfehler","anker":"sec:oekosystem-denkfehler","text":""},{"seite":"oekosystem.html","titel":"3.11 Micro-Quiz","anker":"sec:oekosystem-quiz","text":""},{"seite":"oekosystem.html","titel":"3.12 Selbsttest","anker":"sec:oekosystem-selbsttest","text":""},{"seite":"oekosystem.html","titel":"3.13 Zusammenfassung","anker":"sec:oekosystem-zusammenfassung","text":""},{"seite":"modellierung.html","titel":"Kapitel 4: Vom Management-Wunsch zum Modell","anker":null,"text":""},{"seite":"modellierung.html","titel":"4.1 In 5 Minuten gelöst","anker":"sec:modellierung-schnellstart","text":""},{"seite":"modellierung.html","titel":"4.2 Lernziele","anker":"sec:modellierung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"modellierung.html","titel":"4.3 Ein Satz ist noch kein Modell","anker":"sec:modellierung-vier-fragen","text":"Was aus einer Besprechung mitkommt, klingt meist so:"},{"seite":"modellierung.html","titel":"4.4 Derselbe Datensatz, fünf Modelle","anker":"sec:modellierung-fuenf-modelle","text":"Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14 Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300."},{"seite":"modellierung.html","titel":"Teil 1: Drei Ziele, drei Pläne","anker":"teil-1-drei-ziele-drei-pläne","text":""},{"seite":"modellierung.html","titel":"Teil 2: Ein Satz, drei Lesarten","anker":"teil-2-ein-satz-drei-lesarten","text":"„Stammkunden dürfen wir nicht verlieren.” Jeder im Raum nickt. Niemand merkt, dass drei verschiedene Modelle gemeint sein können:"},{"seite":"modellierung.html","titel":"Teil 3: Hart oder weich","anker":"teil-3-hart-oder-weich","text":"Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:"},{"seite":"modellierung.html","titel":"Teil 4: Ein Quartal später","anker":"teil-4-ein-quartal-später","text":"Die harte Lesart A wurde eingebaut, alles lief drei Monate lang. Dann fragt Weber einen Auftrag über 320 Maschinenstunden an — mehr, als der ganze Zeitraum hergibt."},{"seite":"modellierung.html","titel":"4.5 Der Gesprächsleitfaden","anker":"sec:modellierung-leitfaden","text":"Was aus den vier Teilen als Handwerkszeug bleibt — Fragen, die man in der ersten Besprechung stellt, bevor eine Zeile Code entsteht:"},{"seite":"modellierung.html","titel":"4.6 Übungsaufgaben","anker":"sec:modellierung-uebungsaufgaben","text":""},{"seite":"modellierung.html","titel":"4.7 Finde den Denkfehler","anker":"sec:modellierung-denkfehler","text":""},{"seite":"modellierung.html","titel":"4.8 Micro-Quiz","anker":"sec:modellierung-quiz","text":""},{"seite":"modellierung.html","titel":"4.9 Selbsttest","anker":"sec:modellierung-selbsttest","text":""},{"seite":"modellierung.html","titel":"4.10 Zusammenfassung","anker":"sec:modellierung-zusammenfassung","text":""},{"seite":"lp.html","titel":"Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise","anker":null,"text":""},{"seite":"lp.html","titel":"5.1 In 5 Minuten gelöst","anker":"sec:lp-schnellstart","text":"Bisher hat ein Solver Ihnen gesagt, was Sie tun sollen. Jetzt sagt er Ihnen zusätzlich, was eine zusätzliche Stunde wert wäre — und das ist die Zahl, für die Sie im Betrieb gehört "},{"seite":"lp.html","titel":"5.2 Lernziele","anker":"sec:lp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"lp.html","titel":"5.3 Die Standardform und Schlupfvariablen","anker":"sec:lp-die-standardform-und-schlupfvariablen","text":"Lineare Programme zeichnen sich dadurch aus, dass Zielfunktion und alle Nebenbedingungen strikt linear sind — keine Produkte von Variablen, keine Quadrate, keine Logarithmen."},{"seite":"lp.html","titel":"5.4 Der Simplex-Algorithmus Schritt für Schritt","anker":"sec:lp-der-simplex-algorithmus-schritt-fuer-schritt","text":"Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus Kapitel 2: Das Optimum liegt in einer Ecke. Statt alle Ecken aufzuzählen, wandert er von E"},{"seite":"lp.html","titel":"Der Ablauf","anker":"der-ablauf","text":""},{"seite":"lp.html","titel":"5.5 Das Simplex-Tableau in Python","anker":"sec:lp-das-simplex-tableau-in-python","text":""},{"seite":"lp.html","titel":"5.6 Dualität und Schattenpreise","anker":"sec:lp-dualitaet-und-schattenpreise","text":"Zu jedem linearen Optimierungsproblem (dem primalen Problem) existiert ein spiegelbildliches Zwillingsproblem: das duale Problem."},{"seite":"lp.html","titel":"Der starke Dualitätssatz","anker":"der-starke-dualitätssatz","text":""},{"seite":"lp.html","titel":"Der Schattenpreis als Ableitung","anker":"der-schattenpreis-als-ableitung","text":"Der Dualwert y_i^* ist die Grenzproduktivität der Ressource i:"},{"seite":"lp.html","titel":"5.7 Die Vorzeichenfalle bei Schattenpreisen","anker":"sec:lp-die-vorzeichenfalle-bei-schattenpreisen","text":"Diese Falle sollten Sie sich merken, weil sie in jedem Projekt wieder auftaucht."},{"seite":"lp.html","titel":"5.8 Praxisfall: Sensitivitätsanalyse mit korrekten Schattenpreisen","anker":"sec:lp-praxisfall-sensitivitaetsanalyse-mit-korrekten-schattenpreisen","text":"Szenario. Ein Fertigungsbetrieb stellt drei Produkte her und will wissen:"},{"seite":"lp.html","titel":"5.9 Wann Schattenpreise lügen: Entartung und Toleranzen","anker":"sec:lp-entartung","text":"Der vorige Abschnitt hat gezeigt, wie wertvoll Schattenpreise sind. Dieser zeigt die zwei Fälle, in denen sie eine Entscheidung nicht tragen — und wie man beide erkennt, bevor jema"},{"seite":"lp.html","titel":"Fall 1: Entartung","anker":"fall-1-entartung","text":"Erinnern Sie sich an Abschnitt 2.4: In zwei Dimensionen legen zwei sich schneidende Geraden eine Ecke fest, in drei Dimensionen drei Ebenen, allgemein n Restriktionen bei n Variabl"},{"seite":"lp.html","titel":"Fall 2: Toleranzen","anker":"fall-2-toleranzen","text":"Der zweite Fall ist unscheinbarer und deshalb häufiger. Wie stellt man fest, ob eine Ressource ausgelastet ist? Naheliegend wäre if schlupf == 0. Genau das ist falsch."},{"seite":"lp.html","titel":"Die ehrliche Auskunft: Schattenpreis-Spannen","anker":"die-ehrliche-auskunft-schattenpreis-spannen","text":"Was tut man also, wenn das Modell entartet ist? Man meldet keinen Einzelwert, sondern die Spanne. Die Menge aller optimalen Dualvektoren ist selbst ein Polyeder:"},{"seite":"lp.html","titel":"5.10 Übungsaufgaben","anker":"sec:lp-uebungsaufgaben","text":""},{"seite":"lp.html","titel":"5.11 Finde den Denkfehler","anker":"sec:lp-denkfehler","text":""},{"seite":"lp.html","titel":"5.12 Micro-Quiz","anker":"sec:lp-quiz","text":""},{"seite":"lp.html","titel":"5.13 Selbsttest","anker":"sec:lp-selbsttest","text":""},{"seite":"lp.html","titel":"5.14 Zusammenfassung","anker":"sec:lp-zusammenfassung","text":""},{"seite":"milp.html","titel":"Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound","anker":null,"text":""},{"seite":"milp.html","titel":"6.1 In 5 Minuten gelöst","anker":"sec:milp-schnellstart","text":""},{"seite":"milp.html","titel":"6.2 Lernziele","anker":"sec:milp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"milp.html","titel":"6.3 Warum Runden fundamental scheitert","anker":"sec:milp-warum-runden-fundamental-scheitert","text":"In der realen Welt sind viele Entscheidungen nicht teilbar. Man kann nicht 0,47 Flugzeuge kaufen, keine halbe Lagerhalle bauen, keinen Mitarbeiter zu 38 % einstellen. Noch wichtige"},{"seite":"milp.html","titel":"6.4 Branch-and-Bound","anker":"sec:milp-branch-and-bound","text":"MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen."},{"seite":"milp.html","titel":"Die drei Phasen","anker":"die-drei-phasen","text":""},{"seite":"milp.html","titel":"6.5 Modellierungstricks: Big-M und logische Bedingungen","anker":"sec:milp-modellierungstricks-big-m-und-logische-bedingungen","text":"Hier kommt das Handwerkszeug, das MILP so mächtig macht: Man übersetzt Geschäftsregeln in lineare Ungleichungen — mithilfe binärer Hilfsvariablen y \\in \\{0,1\\} und einer hinreichen"},{"seite":"milp.html","titel":"Muster 1 — Fixkosten / Aktivierungsschalter","anker":"muster-1-fixkosten-aktivierungsschalter","text":"Regel: Wird Maschine j genutzt (x_j > 0), fallen Rüstkosten F_j an."},{"seite":"milp.html","titel":"Muster 2 — Entweder-Oder (disjunctive constraints)","anker":"muster-2-entweder-oder-disjunctive-constraints","text":"Regel: Es muss entweder f(\\mathbf{x}) \\le b_1 oder g(\\mathbf{x}) \\le b_2 gelten."},{"seite":"milp.html","titel":"Muster 3 — Wenn-Dann (Implikation)","anker":"muster-3-wenn-dann-implikation","text":"Regel: Wenn y_1 = 1, dann muss auch y_2 = 1 sein."},{"seite":"milp.html","titel":"Muster 4 — Kardinalität („höchstens K aus N“)","anker":"muster-4-kardinalität-höchstens-k-aus-n","text":"\\sum_{j=1}^N y_j \\le K"},{"seite":"milp.html","titel":"Muster 5 — Semikontinuierlich („entweder 0 oder mindestens L“)","anker":"muster-5-semikontinuierlich-entweder-0-oder-mindestens-l","text":"Regel: Eine Position ist entweder gar nicht besetzt oder mit mindestens L Euro."},{"seite":"milp.html","titel":"6.6 Beispiel: Das Rucksackproblem","anker":"sec:milp-beispiel-das-rucksackproblem","text":"Bevor wir zum Portfolio kommen, das klassische Einstiegsproblem — kurz, verständlich und überall wiederzuerkennen."},{"seite":"milp.html","titel":"6.7 Praxisfall: Portfolio mit Ordergebühren und Kardinalitätsgrenze","anker":"sec:milp-praxisfall-portfolio-mit-ordergebuehren-und-kardinalitaetsgrenze","text":"Szenario. Ein Investor verteilt 100\\,000\\,\\text{€} auf sechs Anlageklassen."},{"seite":"milp.html","titel":"6.8 Wenn der Solver nicht fertig wird: Gap, Zeitlimit und Warm-Start","anker":"sec:milp-gap","text":"Bei einem LP gibt es zwei Ausgänge: eine optimale Lösung oder eine klare Absage. Bei einem MILP gibt es einen dritten, und im Betrieb ist er der häufigste:"},{"seite":"milp.html","titel":"Die zwei Zahlen, die zählen","anker":"die-zwei-zahlen-die-zählen","text":"Branch-and-Bound führt zu jedem Zeitpunkt zwei Werte mit:"},{"seite":"milp.html","titel":"Alle Statusfälle behandeln","anker":"alle-statusfälle-behandeln","text":"Der zweite Teil der Professionalität ist unspektakulär, aber entscheidend: jeden Rückgabewert auswerten, nicht nur OPTIMAL."},{"seite":"milp.html","titel":"Warm-Starts: was sie können und was nicht","anker":"warm-starts-was-sie-können-und-was-nicht","text":"Ein Warm-Start gibt dem Solver eine bekannte Lösung als Startpunkt mit. Die Idee ist verlockend: Wer schon eine brauchbare Lösung hat, muss nicht bei null anfangen."},{"seite":"milp.html","titel":"6.9 Übungsaufgaben","anker":"sec:milp-uebungsaufgaben","text":""},{"seite":"milp.html","titel":"6.10 Finde den Denkfehler","anker":"sec:milp-denkfehler","text":"Die Warnung „M so klein wie möglich“ wird meist mit der Laufzeit begründet: schwache Relaxation, mehr Knoten. Das stimmt — und ist die harmlosere Hälfte der Wahrheit."},{"seite":"milp.html","titel":"6.11 Micro-Quiz","anker":"sec:milp-quiz","text":""},{"seite":"milp.html","titel":"6.12 Selbsttest","anker":"sec:milp-selbsttest","text":""},{"seite":"milp.html","titel":"6.13 Zusammenfassung","anker":"sec:milp-zusammenfassung","text":""},{"seite":"cpsat.html","titel":"Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung","anker":null,"text":""},{"seite":"cpsat.html","titel":"7.1 In 5 Minuten gelöst","anker":"sec:cpsat-schnellstart","text":""},{"seite":"cpsat.html","titel":"7.2 Lernziele","anker":"sec:cpsat-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"cpsat.html","titel":"7.3 Ein anderes Denkmodell","anker":"sec:cpsat-ein-anderes-denkmodell","text":"Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:"},{"seite":"cpsat.html","titel":"Was Propagation leistet","anker":"was-propagation-leistet","text":""},{"seite":"cpsat.html","titel":"7.4 Globale Constraints — die Bausteine von CP-SAT","anker":"sec:cpsat-globale-constraints-die-bausteine-von-cp","text":"CP-SAT stellt mächtige Makros bereit, die in MILP hunderte Big-M-Ungleichungen erfordern würden. Jedes davon hat einen spezialisierten, hocheffizienten Propagator."},{"seite":"cpsat.html","titel":"7.5 Praxisfall: Dynamisches Vertretungssystem","anker":"sec:cpsat-praxisfall-dynamisches-vertretungssystem","text":"Problemstellung. Eine Schule muss nach spontanen Ausfällen vier Unterrichtsstunden nachbesetzen."},{"seite":"cpsat.html","titel":"7.6 Intervallvariablen: Job-Shop-Scheduling","anker":"sec:cpsat-intervallvariablen-job-shop-scheduling","text":"Die Königsdisziplin von CP-SAT sind Zeitplanungsprobleme. Intervallvariablen sind dabei das zentrale Werkzeug."},{"seite":"cpsat.html","titel":"7.7 Die fünf Antworten von CP-SAT","anker":"sec:cpsat-statusfaelle","text":"Abschnitt 6.8 hat gezeigt, warum OPTIMAL bei ganzzahligen Problemen nicht der Normalfall ist. Bei CP-SAT kommt eine Besonderheit hinzu: Es gibt fünf verschiedene Antworten, und dre"},{"seite":"cpsat.html","titel":"7.8 Übungsaufgaben","anker":"sec:cpsat-uebungsaufgaben","text":""},{"seite":"cpsat.html","titel":"7.9 Finde den Denkfehler","anker":"sec:cpsat-denkfehler","text":""},{"seite":"cpsat.html","titel":"7.10 Micro-Quiz","anker":"sec:cpsat-quiz","text":""},{"seite":"cpsat.html","titel":"7.11 Selbsttest","anker":"sec:cpsat-selbsttest","text":""},{"seite":"cpsat.html","titel":"7.12 Zusammenfassung","anker":"sec:cpsat-zusammenfassung","text":""},{"seite":"graphen.html","titel":"Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP","anker":null,"text":""},{"seite":"graphen.html","titel":"8.1 In 5 Minuten gelöst","anker":"sec:graphen-schnellstart","text":""},{"seite":"graphen.html","titel":"8.2 Lernziele","anker":"sec:graphen-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"graphen.html","titel":"8.3 Graphen als Modellsprache","anker":"sec:graphen-graphen-als-modellsprache","text":"Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern Graphen G = (V, E):"},{"seite":"graphen.html","titel":"Das Minimum-Cost-Flow-Problem (MCNFP)","anker":"das-minimum-cost-flow-problem-mcnfp","text":"Minimum-Cost Network Flow Problem — deutsch: kostenminimales Flussproblem. Es ist das mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverteilung."},{"seite":"graphen.html","titel":"8.4 Bipartites Matching: das Zuordnungsproblem","anker":"sec:graphen-bipartites-matching-das-zuordnungsproblem","text":"Wenn N Aufgaben auf N Ressourcen eins zu eins verteilt werden — Orders auf Broker, Schichten auf Mitarbeitende, Aufträge auf Maschinen — spricht man von bipartitem Matching."},{"seite":"graphen.html","titel":"Der Satz von Birkhoff und von Neumann","anker":"der-satz-von-birkhoff-und-von-neumann","text":""},{"seite":"graphen.html","titel":"8.5 Das Vehicle Routing Problem mit Zeitfenstern","anker":"sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern","text":"Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Tim"},{"seite":"graphen.html","titel":"Kurzzyklen verhindern","anker":"kurzzyklen-verhindern","text":"Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt R"},{"seite":"graphen.html","titel":"Praxisbeispiel: Flotten-Routing","anker":"praxisbeispiel-flotten-routing","text":""},{"seite":"graphen.html","titel":"8.6 Übungsaufgaben","anker":"sec:graphen-uebungsaufgaben","text":""},{"seite":"graphen.html","titel":"8.7 Finde den Denkfehler","anker":"sec:graphen-denkfehler","text":""},{"seite":"graphen.html","titel":"8.8 Micro-Quiz","anker":"sec:graphen-quiz","text":""},{"seite":"graphen.html","titel":"8.9 Selbsttest","anker":"sec:graphen-selbsttest","text":""},{"seite":"graphen.html","titel":"8.10 Zusammenfassung","anker":"sec:graphen-zusammenfassung","text":""},{"seite":"metaheuristiken.html","titel":"Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt","anker":null,"text":""},{"seite":"metaheuristiken.html","titel":"9.1 In 5 Minuten gelöst","anker":"sec:metaheuristiken-schnellstart","text":""},{"seite":"metaheuristiken.html","titel":"9.2 Lernziele","anker":"sec:metaheuristiken-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"metaheuristiken.html","titel":"9.3 Die Aufgabe: Rüstzeiten an der Lackieranlage","anker":"sec:metaheuristiken-aufgabe","text":"Der Schnellstart war ein Spielzeug. Die Aufgabe dieses Kapitels ist dieselbe, nur in Betriebsgröße: n Aufträge in eine Reihenfolge bringen, so dass die Summe der Rüstzeiten minimal"},{"seite":"metaheuristiken.html","titel":"Die Instanz","anker":"die-instanz","text":"Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und "},{"seite":"metaheuristiken.html","titel":"9.4 Lokale Suche: der Zug und seine Kosten","anker":"sec:metaheuristiken-lokale-suche","text":"Eine lokale Suche braucht drei Dinge: eine Startlösung, einen Zug und eine Regel, welche Züge übernommen werden."},{"seite":"metaheuristiken.html","titel":"9.5 Simulated Annealing","anker":"sec:metaheuristiken-annealing","text":"Reines Bergsteigen — nur Verbesserungen annehmen — bleibt im ersten lokalen Optimum stehen. Simulated Annealing nimmt Verschlechterungen mit einer Wahrscheinlichkeit an, die von de"},{"seite":"metaheuristiken.html","titel":"Was die Messung zeigt — und was sie nicht zeigt","anker":"was-die-messung-zeigt-und-was-sie-nicht-zeigt","text":"Erstens: Der Beitrag des Annealings ist bescheiden. Bergsteigen kommt auf 917 Minuten, Annealing auf 907 — das simple Verfahren holt 93 % des Ertrags. Das ist kein Argument gegen A"},{"seite":"metaheuristiken.html","titel":"9.6 Ab wann lohnt es sich?","anker":"sec:metaheuristiken-umschlagpunkt","text":"„Kleine Probleme exakt, große mit Metaheuristiken“ ist richtig und nutzlos, solange niemand sagt, wo groß anfängt. Diese Zahl lässt sich messen."},{"seite":"metaheuristiken.html","titel":"Die Spalte, die man nicht weglassen darf","anker":"die-spalte-die-man-nicht-weglassen-darf","text":"Bei 500 Aufträgen haben wir die CP-SAT-Lösung verworfen. Seine Schranke ist trotzdem das Wertvollste in der ganzen Tabelle."},{"seite":"metaheuristiken.html","titel":"9.7 Large Neighborhood Search","anker":"sec:metaheuristiken-lns","text":"Die Tabelle oben endet mit einem Widerspruch. Bei 500 Aufträgen scheitert der exakte Solver an der Größe; die Metaheuristik scheitert an der Kleinheit ihrer Züge — sie verschiebt j"},{"seite":"metaheuristiken.html","titel":"Das Fenster ist die eine Stellschraube","anker":"das-fenster-ist-die-eine-stellschraube","text":""},{"seite":"metaheuristiken.html","titel":"Das Gesamtbild","anker":"das-gesamtbild","text":""},{"seite":"metaheuristiken.html","titel":"9.8 Was dieses Kapitel nicht behandelt","anker":"sec:metaheuristiken-abgrenzung","text":"Damit die Landkarte vollständig ist — drei Verfahren, die in dieselbe Familie gehören und hier bewusst nur benannt werden:"},{"seite":"metaheuristiken.html","titel":"9.9 Übungsaufgaben","anker":"sec:metaheuristiken-uebungsaufgaben","text":""},{"seite":"metaheuristiken.html","titel":"9.10 Finde den Denkfehler","anker":"sec:metaheuristiken-denkfehler","text":""},{"seite":"metaheuristiken.html","titel":"9.11 Micro-Quiz","anker":"sec:metaheuristiken-quiz","text":""},{"seite":"metaheuristiken.html","titel":"9.12 Selbsttest","anker":"sec:metaheuristiken-selbsttest","text":""},{"seite":"metaheuristiken.html","titel":"9.13 Zusammenfassung","anker":"sec:metaheuristiken-zusammenfassung","text":""},{"seite":"dekomposition.html","titel":"Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten","anker":null,"text":""},{"seite":"dekomposition.html","titel":"10.1 In 5 Minuten gelöst","anker":"sec:dekomposition-schnellstart","text":""},{"seite":"dekomposition.html","titel":"10.2 Lernziele","anker":"sec:dekomposition-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"dekomposition.html","titel":"10.3 Warum das naheliegende Modell scheitert","anker":"sec:dekomposition-symmetrie","text":"Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable z_{jr} für „Stück j liegt auf Rolle r“, dazu y_r für „Rolle r wird benutzt”."},{"seite":"dekomposition.html","titel":"10.4 Rechnen mit Mustern, ohne sie aufzuschreiben","anker":"sec:dekomposition-schleife","text":"Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:"},{"seite":"dekomposition.html","titel":"10.5 Das Programm","anker":"sec:dekomposition-programm","text":""},{"seite":"dekomposition.html","titel":"10.6 Was die Zahlen zeigen","anker":"sec:dekomposition-befund","text":""},{"seite":"dekomposition.html","titel":"Die Musterzahl explodiert, der Bedarf nicht","anker":"die-musterzahl-explodiert-der-bedarf-nicht","text":""},{"seite":"dekomposition.html","titel":"Der unbequeme Teil: Es lohnt sich nicht immer","anker":"der-unbequeme-teil-es-lohnt-sich-nicht-immer","text":""},{"seite":"dekomposition.html","titel":"10.7 Übungsaufgaben","anker":"sec:dekomposition-uebungsaufgaben","text":""},{"seite":"dekomposition.html","titel":"10.8 Finde den Denkfehler","anker":"sec:dekomposition-denkfehler","text":""},{"seite":"dekomposition.html","titel":"10.9 Micro-Quiz","anker":"sec:dekomposition-quiz","text":""},{"seite":"dekomposition.html","titel":"10.10 Selbsttest","anker":"sec:dekomposition-selbsttest","text":""},{"seite":"dekomposition.html","titel":"10.11 Zusammenfassung","anker":"sec:dekomposition-zusammenfassung","text":""},{"seite":"qp-nlp.html","titel":"Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität","anker":null,"text":""},{"seite":"qp-nlp.html","titel":"11.1 In 5 Minuten gelöst","anker":"sec:qp-nlp-schnellstart","text":""},{"seite":"qp-nlp.html","titel":"11.2 Lernziele","anker":"sec:qp-nlp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"qp-nlp.html","titel":"Auffrischung: Gradient in einer Minute","anker":"auffrischung-gradient-in-einer-minute","text":"Der Gradient \\nabla f(\\mathbf{x}) ist der Vektor aller partiellen Ableitungen:"},{"seite":"qp-nlp.html","titel":"11.3 Das quadratische Programm (QP)","anker":"sec:qp-nlp-das-quadratische-programm-qp","text":"Ein quadratisches Programm hat eine quadratische Zielfunktion und lineare Nebenbedingungen:"},{"seite":"qp-nlp.html","titel":"Konvexität: die präzise Aussage","anker":"konvexität-die-präzise-aussage","text":"Eine verbreitete, aber zu stark formulierte Aussage lautet: „Wenn \\mathbf{P} positiv semi-definit ist, ist das QP streng konvex; es existiert ein eindeutiges globales Minimum.“ Kor"},{"seite":"qp-nlp.html","titel":"11.4 Die Karush-Kuhn-Tucker-Bedingungen","anker":"sec:qp-nlp-die-karush-kuhn-tucker-bedingungen","text":"Die KKT-Bedingungen verallgemeinern den Lagrange-Ansatz auf Probleme mit Ungleichungen. Sie sind notwendig für ein Optimum — und bei konvexen Problemen auch hinreichend."},{"seite":"qp-nlp.html","titel":"Die vier KKT-Bedingungen","anker":"die-vier-kkt-bedingungen","text":"Am Optimum (\\mathbf{x}^*, \\boldsymbol{\\lambda}^*, \\boldsymbol{\\nu}^*) gilt:"},{"seite":"qp-nlp.html","titel":"11.5 Nichtlineare Optimierung mit scipy.optimize.minimize","anker":"sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize","text":"Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequent"},{"seite":"qp-nlp.html","titel":"Praxisfall: Entropie-maximierte Kapitalallokation","anker":"praxisfall-entropie-maximierte-kapitalallokation","text":"Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:"},{"seite":"qp-nlp.html","titel":"11.6 Jenseits der Konvexität: lokale Optima, Multistart und MINLP","anker":"sec:qp-nlp-nichtkonvex","text":"Alles bisher in diesem Kapitel stand unter einer Bedingung: Konvexität. Sie ist der Grund, warum CVXPY eine Optimalitätsgarantie geben kann und warum die KKT-Bedingungen nicht nur "},{"seite":"qp-nlp.html","titel":"Woher Nicht-Konvexität im Alltag kommt","anker":"woher-nicht-konvexität-im-alltag-kommt","text":"Nicht aus exotischer Mathematik, sondern aus ganz gewöhnlichen betriebswirtschaftlichen Effekten:"},{"seite":"qp-nlp.html","titel":"Was ein lokales Optimum praktisch bedeutet","anker":"was-ein-lokales-optimum-praktisch-bedeutet","text":"scipy.optimize.minimize verweigert nichts. Es rechnet, meldet success: True und liefert eine Zahl. Diese Meldung heißt aber nicht „das ist das Optimum“, sondern nur:"},{"seite":"qp-nlp.html","titel":"Die Zahlen im Klartext","anker":"die-zahlen-im-klartext","text":""},{"seite":"qp-nlp.html","titel":"Ausblick: MINLP und globale Solver","anker":"ausblick-minlp-und-globale-solver","text":"Wird zusätzlich noch ganzzahlig entschieden — „welche Anlage läuft überhaupt“ und „wie stark“ —, entsteht ein MINLP (Mixed-Integer Nonlinear Program), die schwierigste der in diese"},{"seite":"qp-nlp.html","titel":"11.7 Übungsaufgaben","anker":"sec:qp-nlp-uebungsaufgaben","text":""},{"seite":"qp-nlp.html","titel":"11.8 Finde den Denkfehler","anker":"sec:qp-nlp-denkfehler","text":""},{"seite":"qp-nlp.html","titel":"11.9 Micro-Quiz","anker":"sec:qp-nlp-quiz","text":""},{"seite":"qp-nlp.html","titel":"11.10 Selbsttest","anker":"sec:qp-nlp-selbsttest","text":""},{"seite":"qp-nlp.html","titel":"11.11 Zusammenfassung","anker":"sec:qp-nlp-zusammenfassung","text":""},{"seite":"unsicherheit.html","titel":"Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit","anker":null,"text":""},{"seite":"unsicherheit.html","titel":"12.1 In 5 Minuten gelöst","anker":"sec:unsicherheit-schnellstart","text":""},{"seite":"unsicherheit.html","titel":"12.2 Lernziele","anker":"sec:unsicherheit-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"unsicherheit.html","titel":"12.3 Der Fluch des Durchschnitts","anker":"sec:unsicherheit-der-fluch-des-durchschnitts","text":"Der naheliegende Umgang mit Unsicherheit lautet: „Wir setzen den Erwartungswert ein und rechnen deterministisch.“ Das ist fast immer falsch — und zwar nicht ungenau, sondern system"},{"seite":"unsicherheit.html","titel":"Die drei Ansätze im Überblick","anker":"die-drei-ansätze-im-überblick","text":""},{"seite":"unsicherheit.html","titel":"12.4 Monte-Carlo-Simulation","anker":"sec:unsicherheit-monte-carlo-simulation","text":"Monte-Carlo heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan aus, und betrachte die Verteilung der Ergebnisse statt eines einzigen Werts."},{"seite":"unsicherheit.html","titel":"12.5 Zweistufige stochastische Programmierung","anker":"sec:unsicherheit-zweistufige-stochastische-programmierung","text":"Das zweistufige Modell mit Recourse bildet ab, dass Entscheidungen zeitlich gestaffelt sind:"},{"seite":"unsicherheit.html","titel":"12.6 Robuste Optimierung: gegen den Worst Case absichern","anker":"sec:unsicherheit-robuste-optimierung-gegen-den-worst-case","text":"Wenn Wahrscheinlichkeiten unbekannt oder instabil sind — Marktcrashs, Lieferkettenabrisse, Pandemien —, hilft die robuste Optimierung. Sie fragt nicht nach dem Mittel, sondern nach"},{"seite":"unsicherheit.html","titel":"12.7 Übungsaufgaben","anker":"sec:unsicherheit-uebungsaufgaben","text":""},{"seite":"unsicherheit.html","titel":"12.8 Finde den Denkfehler","anker":"sec:unsicherheit-denkfehler","text":""},{"seite":"unsicherheit.html","titel":"12.9 Micro-Quiz","anker":"sec:unsicherheit-quiz","text":""},{"seite":"unsicherheit.html","titel":"12.10 Selbsttest","anker":"sec:unsicherheit-selbsttest","text":""},{"seite":"unsicherheit.html","titel":"12.11 Zusammenfassung","anker":"sec:unsicherheit-zusammenfassung","text":""},{"seite":"dynamische-programmierung.html","titel":"Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution","anker":null,"text":""},{"seite":"dynamische-programmierung.html","titel":"13.1 In 5 Minuten gelöst","anker":"sec:dynamische-programmierung-schnellstart","text":""},{"seite":"dynamische-programmierung.html","titel":"13.2 Lernziele","anker":"sec:dynamische-programmierung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"dynamische-programmierung.html","titel":"13.3 Das Bellmansche Optimalitätsprinzip","anker":"sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip","text":"Richard Bellman formulierte 1957 das Grundprinzip der Dynamischen Programmierung (DP):"},{"seite":"dynamische-programmierung.html","titel":"Die vier Bausteine eines DP-Modells","anker":"die-vier-bausteine-eines-dp-modells","text":""},{"seite":"dynamische-programmierung.html","titel":"Die Bellman-Gleichung","anker":"die-bellman-gleichung","text":"V_t(S_t) = \\min_{a_t\\in\\mathcal{A}(S_t)}\\Big\\{\\,C(S_t,a_t) \\;+\\; \\gamma\\cdot\\mathbb{E}\\big[V_{t+1}(S_{t+1})\\mid S_t,a_t\\big]\\Big\\}"},{"seite":"dynamische-programmierung.html","titel":"13.4 Das Almgren-Chriss-Problem: optimale Orderausführung","anker":"sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung","text":"Ein klassisches OR-Problem an Börsen: Ein institutioneller Händler muss X_0 = 100\\,000 Aktien innerhalb von T Handelsperioden verkaufen."},{"seite":"dynamische-programmierung.html","titel":"13.5 Der Fluch der Dimensionalität","anker":"sec:dynamische-programmierung-der-fluch-der-dimensionalitaet","text":"DP ist mächtig, aber es hat eine harte Grenze. Der Aufwand der Rückwärtsinduktion ist"},{"seite":"dynamische-programmierung.html","titel":"13.6 Übungsaufgaben","anker":"sec:dynamische-programmierung-uebungsaufgaben","text":""},{"seite":"dynamische-programmierung.html","titel":"13.7 Finde den Denkfehler","anker":"sec:dynamische-programmierung-denkfehler","text":"Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder Misserfolg: Was gehört in den Zustand? Ist er zu klein, rechnet das Verfahren völlig korrekt — nur eb"},{"seite":"dynamische-programmierung.html","titel":"13.8 Micro-Quiz","anker":"sec:dynamische-programmierung-quiz","text":""},{"seite":"dynamische-programmierung.html","titel":"13.9 Selbsttest","anker":"sec:dynamische-programmierung-selbsttest","text":""},{"seite":"dynamische-programmierung.html","titel":"13.10 Zusammenfassung","anker":"sec:dynamische-programmierung-zusammenfassung","text":""},{"seite":"mehrziel.html","titel":"Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte","anker":null,"text":""},{"seite":"mehrziel.html","titel":"14.1 In 5 Minuten gelöst","anker":"sec:mehrziel-schnellstart","text":""},{"seite":"mehrziel.html","titel":"14.2 Lernziele","anker":"sec:mehrziel-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"mehrziel.html","titel":"14.3 Wann ein Kompromiss gut ist","anker":"sec:mehrziel-dominanz","text":"Bei einem Ziel ist „besser” eindeutig. Bei zwei Zielen nicht mehr: Ein Plan kann billiger und schmutziger sein als ein anderer, und dann ist keiner von beiden besser."},{"seite":"mehrziel.html","titel":"14.4 Der Reflex: alles in ein Ziel rühren","anker":"sec:mehrziel-skalarisierung","text":"Der naheliegende Weg, zwei Ziele zu einem zu machen, ist die gewichtete Summe:"},{"seite":"mehrziel.html","titel":"14.5 Das Programm","anker":"sec:mehrziel-programm","text":"Zwölf Sendungen, drei Verkehrsträger, fünf freie Bahntrassen. Der Zielkonflikt entsteht nicht zwischen den Trägern — die Bahn ist immer billiger und sauberer als der LKW —, sondern"},{"seite":"mehrziel.html","titel":"14.6 Was die gewichtete Summe nicht sieht","anker":"sec:mehrziel-luecke","text":"Die Front hat zehn Punkte. Ein Raster aus 1 401 Gewichten findet sechs davon. Vier Kompromisse sind für kein Gewicht erreichbar — und das ist keine Frage der Rasterweite."},{"seite":"mehrziel.html","titel":"Das Gewicht ist keine Feineinstellung","anker":"das-gewicht-ist-keine-feineinstellung","text":"Die zweite Beobachtung aus derselben Tabelle: 1 401 Gewichte erzeugen sechs verschiedene Pläne. Weite Gewichtsbereiche liefern dasselbe Ergebnis, und dazwischen springt es."},{"seite":"mehrziel.html","titel":"14.7 Das ε-Constraint-Verfahren","anker":"sec:mehrziel-epsilon","text":"Der Ausweg ist unspektakulär: Man macht aus dem zweiten Ziel eine Nebenbedingung."},{"seite":"mehrziel.html","titel":"Wenn es eine klare Rangfolge gibt: lexikografisch","anker":"wenn-es-eine-klare-rangfolge-gibt-lexikografisch","text":"Manchmal ist ein Ziel wirklich vorrangig. Dann optimiert man lexikografisch: erst das erste Ziel, dann das zweite unter der Bedingung, dass das erste (fast) erhalten bleibt."},{"seite":"mehrziel.html","titel":"14.8 Wie man die Front vorlegt","anker":"sec:mehrziel-vorlegen","text":"Eine Front mit zehn Punkten ist kein Ergebnis, sondern eine Speisekarte. Damit sie entscheidbar wird, gehört eine Spalte dazu, die im Programm EUR je kg heißt:"},{"seite":"mehrziel.html","titel":"14.9 Übungsaufgaben","anker":"sec:mehrziel-uebungsaufgaben","text":""},{"seite":"mehrziel.html","titel":"14.10 Finde den Denkfehler","anker":"sec:mehrziel-denkfehler","text":""},{"seite":"mehrziel.html","titel":"14.11 Micro-Quiz","anker":"sec:mehrziel-quiz","text":""},{"seite":"mehrziel.html","titel":"14.12 Selbsttest","anker":"sec:mehrziel-selbsttest","text":""},{"seite":"mehrziel.html","titel":"14.13 Zusammenfassung","anker":"sec:mehrziel-zusammenfassung","text":""},{"seite":"prognose.html","titel":"Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung","anker":null,"text":""},{"seite":"prognose.html","titel":"15.1 In 5 Minuten gelöst","anker":"sec:prognose-schnellstart","text":""},{"seite":"prognose.html","titel":"15.2 Lernziele","anker":"sec:prognose-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"prognose.html","titel":"15.3 Die Naht zwischen zwei Abteilungen","anker":"sec:prognose-naht","text":"In fast jedem Unternehmen sieht der Ablauf so aus:"},{"seite":"prognose.html","titel":"15.4 Das Programm","anker":"sec:prognose-programm","text":"Dieselbe Bäckerei, aber mit Merkmalen: Wochentag, Temperatur, Aktionstage. Die Nachfrage muss jetzt modelliert werden."},{"seite":"prognose.html","titel":"15.5 Der Befund","anker":"sec:prognose-befund","text":""},{"seite":"prognose.html","titel":"Warum ein pauschaler Zuschlag zu kurz greift","anker":"warum-ein-pauschaler-zuschlag-zu-kurz-greift","text":"Die zweite Zeile der Tabelle ist die verbreitete Praxis: Punktprognose plus Sicherheitsbestand. Sie hilft (−7,3 %), bleibt aber hinter der Quantilregression zurück. Das Programm re"},{"seite":"prognose.html","titel":"15.6 Eine Messfalle, in die der Autor selbst getappt ist","anker":"sec:prognose-messfalle","text":"Der erste Entwurf dieses Programms bewertete auf 230 Testtagen — ein realistischer Zeitraum, gut sieben Monate. Dort hatte die Quantilregression den besseren MSE, und die ganze Aus"},{"seite":"prognose.html","titel":"15.7 Wie weit das Verfahren reicht","anker":"sec:prognose-ausblick","text":"Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert” — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: das Prognosemodell direkt auf die "},{"seite":"prognose.html","titel":"15.8 Übungsaufgaben","anker":"sec:prognose-uebungsaufgaben","text":""},{"seite":"prognose.html","titel":"15.9 Finde den Denkfehler","anker":"sec:prognose-denkfehler","text":""},{"seite":"prognose.html","titel":"15.10 Micro-Quiz","anker":"sec:prognose-quiz","text":""},{"seite":"prognose.html","titel":"15.11 Selbsttest","anker":"sec:prognose-selbsttest","text":""},{"seite":"prognose.html","titel":"15.12 Zusammenfassung","anker":"sec:prognose-zusammenfassung","text":""},{"seite":"bruecke.html","titel":"Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten","anker":null,"text":""},{"seite":"bruecke.html","titel":"16.1 In 5 Minuten gelöst","anker":"sec:bruecke-schnellstart","text":""},{"seite":"bruecke.html","titel":"16.2 Lernziele","anker":"sec:bruecke-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"bruecke.html","titel":"16.3 Das Programm","anker":"sec:bruecke-programm","text":""},{"seite":"bruecke.html","titel":"16.4 Die zwei Brücken im Einzelnen","anker":"sec:bruecke-einzelnen","text":""},{"seite":"bruecke.html","titel":"Erste Brücke: Allokation","anker":"erste-brücke-allokation","text":""},{"seite":"bruecke.html","titel":"Zweite Brücke: Absicherung gegen den schlechtesten Fall","anker":"zweite-brücke-absicherung-gegen-den-schlechtesten-fall","text":"Hier ist die Übereinstimmung noch enger, weil sie nicht nur die Struktur betrifft, sondern die Formel. Die Rockafellar-Uryasev-Formulierung aus Kapitel 20"},{"seite":"bruecke.html","titel":"Wo die Lösungen sich unterscheiden","anker":"wo-die-lösungen-sich-unterscheiden","text":"Der Vergleich fördert nebenbei einen Unterschied zutage, den man nicht erwartet: Die Streuungsgrenze von 40 % bindet nur im Einkauf, nicht im Depot."},{"seite":"bruecke.html","titel":"16.5 Wo die Brücke endet","anker":"sec:bruecke-grenzen","text":"Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Dinge wandern nicht mit:"},{"seite":"bruecke.html","titel":"16.6 Übungsaufgaben","anker":"sec:bruecke-uebungsaufgaben","text":""},{"seite":"bruecke.html","titel":"16.7 Finde den Denkfehler","anker":"sec:bruecke-denkfehler","text":""},{"seite":"bruecke.html","titel":"16.8 Micro-Quiz","anker":"sec:bruecke-quiz","text":""},{"seite":"bruecke.html","titel":"16.9 Selbsttest","anker":"sec:bruecke-selbsttest","text":""},{"seite":"bruecke.html","titel":"16.10 Zusammenfassung","anker":"sec:bruecke-zusammenfassung","text":""},{"seite":"supplychain.html","titel":"Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit","anker":null,"text":""},{"seite":"supplychain.html","titel":"17.1 In 5 Minuten gelöst","anker":"sec:supplychain-schnellstart","text":""},{"seite":"supplychain.html","titel":"17.2 Lernziele","anker":"sec:supplychain-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"supplychain.html","titel":"17.3 Die Aufgabe","anker":"sec:supplychain-aufgabe","text":"Fünf Blöcke, 24 Stunden, eine Last zwischen 270 und 804 MW. Dazu Windeinspeisung, die niemand am Vorabend kennt."},{"seite":"supplychain.html","titel":"Die Zeitstruktur der Entscheidung","anker":"die-zeitstruktur-der-entscheidung","text":"Hier geht das Kapitel über Abschnitt 12.5 hinaus:"},{"seite":"supplychain.html","titel":"17.4 Das Programm","anker":"sec:supplychain-programm","text":""},{"seite":"supplychain.html","titel":"17.5 Der Befund","anker":"sec:supplychain-befund","text":""},{"seite":"supplychain.html","titel":"17.6 Was Versorgungssicherheit kostet","anker":"sec:supplychain-sicherheit","text":"Der dritte Plan legt eine CVaR-Schranke auf die nicht gedeckte Energie: Im Mittel der schlechtesten 10 % der Szenarien darf nichts fehlen. Dieselbe Rockafellar-Uryasev-Konstruktion"},{"seite":"supplychain.html","titel":"17.7 Übungsaufgaben","anker":"sec:supplychain-uebungsaufgaben","text":""},{"seite":"supplychain.html","titel":"17.8 Finde den Denkfehler","anker":"sec:supplychain-denkfehler","text":""},{"seite":"supplychain.html","titel":"17.9 Micro-Quiz","anker":"sec:supplychain-quiz","text":""},{"seite":"supplychain.html","titel":"17.10 Selbsttest","anker":"sec:supplychain-selbsttest","text":""},{"seite":"supplychain.html","titel":"17.11 Zusammenfassung","anker":"sec:supplychain-zusammenfassung","text":""},{"seite":"finanzdaten.html","titel":"Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage","anker":null,"text":""},{"seite":"finanzdaten.html","titel":"18.1 In 5 Minuten gelöst","anker":"sec:finanzdaten-schnellstart","text":""},{"seite":"finanzdaten.html","titel":"18.2 Lernziele","anker":"sec:finanzdaten-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"finanzdaten.html","titel":"18.3 Diskrete und logarithmische Renditen","anker":"sec:finanzdaten-diskrete-und-logarithmische-renditen","text":"Sei P_{i,t} der bereinigte Schlusskurs (adjusted close) von Titel i zum Zeitpunkt t. „Bereinigt“ heißt: um Dividenden und Aktiensplits korrigiert — sonst erscheint jede Dividendenz"},{"seite":"finanzdaten.html","titel":"Diskrete Rendite","anker":"diskrete-rendite","text":"R_{i,t} = \\frac{P_{i,t} - P_{i,t-1}}{P_{i,t-1}} = \\frac{P_{i,t}}{P_{i,t-1}} - 1"},{"seite":"finanzdaten.html","titel":"Logarithmische Rendite","anker":"logarithmische-rendite","text":"r_{i,t} = \\ln\\!\\left(\\frac{P_{i,t}}{P_{i,t-1}}\\right) = \\ln P_{i,t} - \\ln P_{i,t-1}"},{"seite":"finanzdaten.html","titel":"18.4 Das Schätzfehler-Problem","anker":"sec:finanzdaten-das-schaetzfehler-problem","text":"Sei \\mathbf{X}\\in\\mathbb{R}^{T\\times N} die Matrix zentrierter Renditen von N Titeln über T Handelstage. Die Stichproben-Kovarianzmatrix lautet:"},{"seite":"finanzdaten.html","titel":"18.5 Ledoit-Wolf-Shrinkage","anker":"sec:finanzdaten-ledoit-wolf-shrinkage","text":"Olivier Ledoit und Michael Wolf lösten das Dilemma durch lineare Schrumpfung:"},{"seite":"finanzdaten.html","titel":"Welches Ziel \\mathbf{F}?","anker":"welches-ziel-mathbff","text":"Ein häufiges Missverständnis: Es gibt nämlich mehrere Ledoit-Wolf-Varianten, und Text und Code müssen sich konsistent auf dieselbe beziehen:"},{"seite":"finanzdaten.html","titel":"18.6 Praxis: Datenpipeline mit korrekter Spaltenreihenfolge","anker":"sec:finanzdaten-praxis-datenpipeline-mit-korrekter-spaltenreihenfolge","text":""},{"seite":"finanzdaten.html","titel":"18.7 Übungsaufgaben","anker":"sec:finanzdaten-uebungsaufgaben","text":""},{"seite":"finanzdaten.html","titel":"18.8 Finde den Denkfehler","anker":"sec:finanzdaten-denkfehler","text":""},{"seite":"finanzdaten.html","titel":"18.9 Micro-Quiz","anker":"sec:finanzdaten-quiz","text":""},{"seite":"finanzdaten.html","titel":"18.10 Selbsttest","anker":"sec:finanzdaten-selbsttest","text":""},{"seite":"finanzdaten.html","titel":"18.11 Zusammenfassung","anker":"sec:finanzdaten-zusammenfassung","text":""},{"seite":"markowitz.html","titel":"Kapitel 19: Die moderne Portfoliotheorie nach Markowitz","anker":null,"text":""},{"seite":"markowitz.html","titel":"19.1 In 5 Minuten gelöst","anker":"sec:markowitz-schnellstart","text":""},{"seite":"markowitz.html","titel":"19.2 Lernziele","anker":"sec:markowitz-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"markowitz.html","titel":"19.3 Warum Diversifikation funktioniert","anker":"sec:markowitz-warum-diversifikation-funktioniert","text":"Harry Markowitz’ zentrale Einsicht von 1952: Das Risiko eines Portfolios ist nicht die Summe der Einzelrisiken. Es hängt davon ab, wie die Titel zusammenspielen."},{"seite":"markowitz.html","titel":"19.4 Das Mean-Variance-Modell","anker":"sec:markowitz-das-mean-variance-modell","text":"Sei \\mathbf{w} = (w_1,\\dots,w_n)^\\top der Vektor der Portfoliogewichte:"},{"seite":"markowitz.html","titel":"Die Effizienzgrenze und das Tangentialportfolio","anker":"die-effizienzgrenze-und-das-tangentialportfolio","text":"Die Effizienzgrenze (efficient frontier) ist die Kurve aller Portfolios, die zu jedem Risikoniveau die höchstmögliche Rendite liefern."},{"seite":"markowitz.html","titel":"Die Korn-Transformation","anker":"die-korn-transformation","text":"Die Sharpe Ratio ist ein Bruch — und damit nicht konvex. CVXPY würde sie ablehnen. Der Ausweg ist ein eleganter Trick, der die Homogenität des Problems ausnutzt:"},{"seite":"markowitz.html","titel":"19.5 Institutionelle Nebenbedingungen","anker":"sec:markowitz-institutionelle-nebenbedingungen","text":"In der Praxis gelten regulatorische und interne Regeln:"},{"seite":"markowitz.html","titel":"Was diese Regeln kosten","anker":"was-diese-regeln-kosten","text":"Jede Regel schneidet etwas vom Erreichbaren ab. Wie viel, lässt sich ausrechnen — man legt die Effizienzlinie ohne und mit Beschränkungen übereinander:"},{"seite":"markowitz.html","titel":"19.6 Vollimplementierung mit CVXPY","anker":"sec:markowitz-vollimplementierung-mit-cvxpy","text":""},{"seite":"markowitz.html","titel":"19.7 Übungsaufgaben","anker":"sec:markowitz-uebungsaufgaben","text":""},{"seite":"markowitz.html","titel":"19.8 Finde den Denkfehler","anker":"sec:markowitz-denkfehler","text":"Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der "},{"seite":"markowitz.html","titel":"19.9 Micro-Quiz","anker":"sec:markowitz-quiz","text":""},{"seite":"markowitz.html","titel":"19.10 Selbsttest","anker":"sec:markowitz-selbsttest","text":""},{"seite":"markowitz.html","titel":"19.11 Zusammenfassung","anker":"sec:markowitz-zusammenfassung","text":""},{"seite":"cvar.html","titel":"Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten","anker":null,"text":""},{"seite":"cvar.html","titel":"20.1 In 5 Minuten gelöst","anker":"sec:cvar-schnellstart","text":""},{"seite":"cvar.html","titel":"20.2 Lernziele","anker":"sec:cvar-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"cvar.html","titel":"20.3 Die zwei Schwächen des Markowitz-Modells","anker":"sec:cvar-die-zwei-schwaechen-des-markowitz-modells","text":"Erstens: die Normalverteilungs-Illusion. Die Varianz behandelt Aufwärts- und Abwärtsschwankungen gleich und unterstellt implizit symmetrische, dünn auslaufende Verteilungen. Reale "},{"seite":"cvar.html","titel":"20.4 Value at Risk und Conditional Value at Risk","anker":"sec:cvar-value-at-risk-und-conditional-value","text":""},{"seite":"cvar.html","titel":"Das Rockafellar-Uryasev-Theorem (2000)","anker":"das-rockafellar-uryasev-theorem-2000","text":"Rockafellar und Uryasev zeigten, dass sich der CVaR über S Szenarien exakt als lineares Programm formulieren lässt:"},{"seite":"cvar.html","titel":"20.5 Transaktionskosten über die L_1-Norm","anker":"sec:cvar-transaktionskosten-ueber-die-l-1-norm","text":"Sei \\mathbf{w}_{\\text{alt}} das bestehende Portfolio und \\mathbf{w} das neue Ziel. Der Umschlag (turnover) ist:"},{"seite":"cvar.html","titel":"20.6 Implementierung: CVaR-Portfolio mit Reibung","anker":"sec:cvar-implementierung-cvar-portfolio-mit-reibung","text":""},{"seite":"cvar.html","titel":"20.7 Übungsaufgaben","anker":"sec:cvar-uebungsaufgaben","text":""},{"seite":"cvar.html","titel":"20.8 Finde den Denkfehler","anker":"sec:cvar-denkfehler","text":""},{"seite":"cvar.html","titel":"20.9 Micro-Quiz","anker":"sec:cvar-quiz","text":""},{"seite":"cvar.html","titel":"20.10 Selbsttest","anker":"sec:cvar-selbsttest","text":""},{"seite":"cvar.html","titel":"20.11 Zusammenfassung","anker":"sec:cvar-zusammenfassung","text":""},{"seite":"handelsmaschine.html","titel":"Kapitel 21: Die vollständige quantitative Handelsmaschine","anker":null,"text":""},{"seite":"handelsmaschine.html","titel":"21.1 In 5 Minuten gelöst","anker":"sec:handelsmaschine-schnellstart","text":""},{"seite":"handelsmaschine.html","titel":"21.2 Lernziele","anker":"sec:handelsmaschine-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"handelsmaschine.html","titel":"21.3 Die Architektur","anker":"sec:handelsmaschine-die-architektur","text":""},{"seite":"handelsmaschine.html","titel":"21.4 Rebalancing-Termine richtig bestimmen","anker":"sec:handelsmaschine-rebalancing-termine-richtig-bestimmen","text":""},{"seite":"handelsmaschine.html","titel":"21.5 Die Engine","anker":"sec:handelsmaschine-die-engine","text":""},{"seite":"handelsmaschine.html","titel":"21.6 Die fünf Selbsttäuschungen des Backtestens","anker":"sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens","text":""},{"seite":"handelsmaschine.html","titel":"21.7 Vom Backtest zum Betrieb","anker":"sec:handelsmaschine-vom-backtest-zum-betrieb","text":"Ein guter Backtest ist eine notwendige, keine hinreichende Bedingung. Was zwischen Backtest und echtem Einsatz liegt:"},{"seite":"handelsmaschine.html","titel":"21.8 Übungsaufgaben","anker":"sec:handelsmaschine-uebungsaufgaben","text":""},{"seite":"handelsmaschine.html","titel":"21.9 Finde den Denkfehler","anker":"sec:handelsmaschine-denkfehler","text":"Backtest_Fallen.py zeigt drei Fehler, die im Backtest stecken: Lookahead, Survivorship, vergessene Kosten. Alle drei kann man einem Programm ansehen, wenn man es liest."},{"seite":"handelsmaschine.html","titel":"21.10 Micro-Quiz","anker":"sec:handelsmaschine-quiz","text":""},{"seite":"handelsmaschine.html","titel":"21.11 Selbsttest","anker":"sec:handelsmaschine-selbsttest","text":""},{"seite":"handelsmaschine.html","titel":"21.12 Zusammenfassung","anker":"sec:handelsmaschine-zusammenfassung","text":""},{"seite":"praxisfallen.html","titel":"Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz","anker":null,"text":""},{"seite":"praxisfallen.html","titel":"22.1 In 5 Minuten gelöst","anker":"sec:praxisfallen-schnellstart","text":"Die vorangegangenen vierzehn Kapitel enden fast alle mit derselben Empfehlung: Prüfen Sie das Ergebnis gegen die Wirklichkeit. Dieses Kapitel beginnt damit, diese Prüfung als wiede"},{"seite":"praxisfallen.html","titel":"22.2 Lernziele","anker":"sec:praxisfallen-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"praxisfallen.html","titel":"22.3 Die fünf typischen Praxisfallen","anker":"sec:praxisfallen-die-fuenf-typischen-praxisfallen","text":""},{"seite":"praxisfallen.html","titel":"Falle 1 — Infeasibility","anker":"falle-1-infeasibility","text":"Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE."},{"seite":"praxisfallen.html","titel":"Falle 2 — Der Black-Box-Effekt","anker":"falle-2-der-black-box-effekt","text":"Ein Planer oder Trader lehnt ein System ab, wenn er die Zuweisungen nicht nachvollziehen kann. Akzeptanz ist keine Nebensache, sondern Voraussetzung für den Betrieb."},{"seite":"praxisfallen.html","titel":"Falle 3 — Regimewechsel und Schätzfehler","anker":"falle-3-regimewechsel-und-schätzfehler","text":"Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine Liquiditätskrise und keine Zinswende."},{"seite":"praxisfallen.html","titel":"Falle 4 — Lookahead- und Survivorship-Bias","anker":"falle-4-lookahead--und-survivorship-bias","text":"Ausführlich in Kapitel 21 behandelt. Die Kurzfassung: Beide Fehler machen Backtests systematisch zu gut, ohne eine Fehlermeldung zu erzeugen. Der Lookahead-Selbsttest aus Abschnitt"},{"seite":"praxisfallen.html","titel":"Falle 5 — Laufzeitexplosion","anker":"falle-5-laufzeitexplosion","text":"Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße."},{"seite":"praxisfallen.html","titel":"22.4 Architektur einer produktionsreifen OR-Plattform","anker":"sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform","text":""},{"seite":"praxisfallen.html","titel":"22.5 Der gemeinsame Unterbau: or_kern.py","anker":"sec:praxisfallen-or-kern","text":"Die fünf Prinzipien oben beschreiben eine Plattform. Der Weg dorthin beginnt aber viel kleiner — mit der Frage, welche Teile eines Optimierungsprogramms immer dieselben sind."},{"seite":"praxisfallen.html","titel":"Die vier Schichten","anker":"die-vier-schichten","text":"\\underbrace{\\text{Rohdaten}}_{\\text{Excel, CSV, ERP}}\n\\;\\longrightarrow\\;\n\\underbrace{\\text{Domänenmodell}}_{\\text{geprüft, solverfrei}}\n\\;\\longrightarrow\\;\n\\underbrace{\\text{Model"},{"seite":"praxisfallen.html","titel":"Ein Enum für fünf Bibliotheken","anker":"ein-enum-für-fünf-bibliotheken","text":"Der unscheinbarste, aber wirksamste Teil ist die Statusübersetzung. Dieselbe Aussage heißt in den fünf im Buch verwendeten Bibliotheken:"},{"seite":"praxisfallen.html","titel":"22.6 Der Solverwechsel in der Praxis","anker":"sec:praxisfallen-solverwechsel","text":"Der vorige Abschnitt endet mit einer Behauptung: Ein Solverwechsel betreffe genau einen Baustein. Das ist die Art Satz, die in jedem Architekturvortrag vorkommt und die man deshalb"},{"seite":"praxisfallen.html","titel":"22.7 Eine Checkliste vor dem Produktivgang","anker":"sec:praxisfallen-eine-checkliste-vor-dem-produktivgang","text":"Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:"},{"seite":"praxisfallen.html","titel":"22.8 Weiterführende Literatur und Roadmap","anker":"sec:praxisfallen-weiterfuehrende-literatur-und-roadmap","text":"Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Int"},{"seite":"praxisfallen.html","titel":"22.9 Übungsaufgaben","anker":"sec:praxisfallen-uebungsaufgaben","text":""},{"seite":"praxisfallen.html","titel":"22.10 Finde den Denkfehler","anker":"sec:praxisfallen-denkfehler","text":"Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts."},{"seite":"praxisfallen.html","titel":"22.11 Micro-Quiz","anker":"sec:praxisfallen-quiz","text":""},{"seite":"praxisfallen.html","titel":"22.12 Selbsttest","anker":"sec:praxisfallen-selbsttest","text":""},{"seite":"praxisfallen.html","titel":"22.13 Zusammenfassung","anker":"sec:praxisfallen-zusammenfassung","text":""},{"seite":"testing.html","titel":"Kapitel 23: Testen, Messen, Ausliefern","anker":null,"text":""},{"seite":"testing.html","titel":"23.1 In 5 Minuten gelöst","anker":"sec:testing-schnellstart","text":""},{"seite":"testing.html","titel":"23.2 Lernziele","anker":"sec:testing-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"testing.html","titel":"23.3 Warum Optimierungsmodelle schwer zu testen sind","anker":"sec:testing-warum-schwer","text":"Bei einer gewöhnlichen Funktion schreibt man den erwarteten Wert hin:"},{"seite":"testing.html","titel":"23.4 Die Testsuite","anker":"sec:testing-suite","text":""},{"seite":"testing.html","titel":"23.5 Wer testet die Tests?","anker":"sec:testing-mutation","text":"33 grüne Tests. Das ist noch kein Ergebnis."},{"seite":"testing.html","titel":"Der erste Lauf fand zwei Lücken","anker":"der-erste-lauf-fand-zwei-lücken","text":"Die Suite hatte zunächst 31 Tests. Der Mutationstest sagte dazu:"},{"seite":"testing.html","titel":"23.6 Ein Vergleich, dem man glauben kann","anker":"sec:testing-benchmark","text":"Solververgleiche stehen in jedem Blog, und die meisten sind wertlos. Nicht, weil falsch gemessen wurde, sondern weil zu wenig dazugesagt wird."},{"seite":"testing.html","titel":"Die interessanteste Spalte heißt „Anteil”","anker":"die-interessanteste-spalte-heißt-anteil","text":"Sie sagt, wie viel der Zeit in den Aufbau des Modells geht statt ins Lösen — und die Zahlen sind unbequem:"},{"seite":"testing.html","titel":"23.7 Das Modell als Dienst","anker":"sec:testing-dienst","text":"Ein Modell hinter eine HTTP-Schnittstelle zu hängen sieht nach einer Fingerübung aus. Es gibt aber einen Unterschied, der die ganze Bauform bestimmt:"},{"seite":"testing.html","titel":"Threads oder Prozesse? Eine Messung, keine Meinung","anker":"threads-oder-prozesse-eine-messung-keine-meinung","text":"Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL w"},{"seite":"testing.html","titel":"Was im echten Betrieb dazukommt","anker":"was-im-echten-betrieb-dazukommt","text":"Das Beispiel hält das Auftragsbuch in einem dict und rechnet in Threads desselben Prozesses. Für einen einzelnen Rechner reicht das; darüber hinaus nicht:"},{"seite":"testing.html","titel":"23.8 Übungsaufgaben","anker":"sec:testing-uebungsaufgaben","text":""},{"seite":"testing.html","titel":"23.9 Finde den Denkfehler","anker":"sec:testing-denkfehler","text":""},{"seite":"testing.html","titel":"23.10 Micro-Quiz","anker":"sec:testing-quiz","text":""},{"seite":"testing.html","titel":"23.11 Selbsttest","anker":"sec:testing-selbsttest","text":""},{"seite":"testing.html","titel":"23.12 Zusammenfassung","anker":"sec:testing-zusammenfassung","text":""},{"seite":"projektwerkstatt.html","titel":"Projektwerkstatt","anker":null,"text":""},{"seite":"projektwerkstatt.html","titel":"Wie Sie ein Projekt bearbeiten","anker":"wie-sie-ein-projekt-bearbeiten","text":"Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und einigen gelungenen Projekten destilliert:"},{"seite":"projektwerkstatt.html","titel":"P1 — Vertretungsplaner für eine Schule","anker":"p1-vertretungsplaner-für-eine-schule","text":""},{"seite":"projektwerkstatt.html","titel":"P2 — Schichtplanung für ein Pflegeteam","anker":"p2-schichtplanung-für-ein-pflegeteam","text":""},{"seite":"projektwerkstatt.html","titel":"P3 — Tourenplanung für einen Lieferdienst","anker":"p3-tourenplanung-für-einen-lieferdienst","text":""},{"seite":"projektwerkstatt.html","titel":"P4 — Standort- und Lagernetzplanung","anker":"p4-standort--und-lagernetzplanung","text":""},{"seite":"projektwerkstatt.html","titel":"P5 — Produktionsplanung mit Rüstkosten","anker":"p5-produktionsplanung-mit-rüstkosten","text":""},{"seite":"projektwerkstatt.html","titel":"P6 — Portfolio-Rebalancer für ein Privatdepot","anker":"p6-portfolio-rebalancer-für-ein-privatdepot","text":""},{"seite":"projektwerkstatt.html","titel":"P7 — Risikoreport mit CVaR und Stresstests","anker":"p7-risikoreport-mit-cvar-und-stresstests","text":""},{"seite":"projektwerkstatt.html","titel":"P9 — Wenn der Solver aussteigt: Tourenplanung in Echtgröße","anker":"p9-wenn-der-solver-aussteigt-tourenplanung-in-echtgröße","text":""},{"seite":"projektwerkstatt.html","titel":"P10 — Zwei Ziele, eine Entscheidung: Kosten gegen CO₂","anker":"p10-zwei-ziele-eine-entscheidung-kosten-gegen-co₂","text":""},{"seite":"projektwerkstatt.html","titel":"P11 — Vom Skript zum Dienst: das Modell übergeben","anker":"p11-vom-skript-zum-dienst-das-modell-übergeben","text":""},{"seite":"projektwerkstatt.html","titel":"P8 — Freies Projekt aus Ihrem eigenen Umfeld","anker":"p8-freies-projekt-aus-ihrem-eigenen-umfeld","text":""},{"seite":"projektwerkstatt.html","titel":"Eine Bitte zum Schluss","anker":"eine-bitte-zum-schluss","text":"Wenn Sie ein Projekt fertig haben, machen Sie zwei Dinge:"},{"seite":"anhang-loesungen.html","titel":"Anhang A: Lösungen zu allen Übungsaufgaben","anker":null,"text":""},{"seite":"anhang-loesungen.html","titel":"A.1 Lösungen zu Kapitel „Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung“","anker":"sec:loesungen-einfuehrung","text":"1.1 — Analytik-Stufen. (a) deskriptiv (Vergangenheit beschreiben). (b) prädiktiv (Prognose). (c) präskriptiv (Handlungsanweisung unter Restriktionen). (d) prädiktiv (Klassifikation"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 1.1 — Die Schreinerei verdoppelt ihren Gewinn","anker":"finde-den-denkfehler-1.1-die-schreinerei-verdoppelt-ihren-gewinn","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-einfuehrung","text":"1 — (c) präskriptiv. Die Frage lautet „was tun“, und es gibt eine Restriktion (das Restbudget). Dass man für die Bewertung eine Prognose braucht, macht die Frage nicht prädiktiv — "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-einfuehrung","text":""},{"seite":"anhang-loesungen.html","titel":"A.2 Lösungen zu Kapitel „Das mathematische Fundament — Vektoren, Matrizen, Konvexität“","anker":"sec:loesungen-fundament","text":"2.1 — Matrixform lesen. \\max 4x_1 + x_2 + 6x_3 u. d. N. x_1 + 2x_2 \\le 10, x_2 + 3x_3 \\le 12, x \\ge 0. 3 Variablen, 2 Nebenbedingungen (plus Nichtnegativität)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 2.1 — Die unauffällige Transposition","anker":"finde-den-denkfehler-2.1-die-unauffällige-transposition","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-fundament","text":"1 — (b). Der Fundamentalsatz sagt nur, dass ein Optimum in einer Ecke angenommen wird. (a) ist falsch, weil es mehrere optimale Ecken geben kann (wenn die Zielfunktion parallel zu "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-fundament","text":""},{"seite":"anhang-loesungen.html","titel":"A.3 Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“","anker":"sec:loesungen-oekosystem","text":"3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binä"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 3.1 — Der Solver, der angeblich dreimal schneller ist","anker":"finde-den-denkfehler-3.1-der-solver-der-angeblich-dreimal-schneller-ist","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-oekosystem","text":"1 — (b) CP-SAT. Es geht um diskrete Zuweisung mit logischen Regeln („nicht gleichzeitig“) — CP-SATs Kerngebiet, und Regeln dieser Art formuliert man dort direkt statt über Big-M-Um"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-oekosystem","text":""},{"seite":"anhang-loesungen.html","titel":"A.4 Lösungen zu Kapitel „Vom Management-Wunsch zum Modell“","anker":"sec:loesungen-modellierung","text":"Die fehlende Lesart. Eine dritte Lesart: „Der Umsatzanteil der Stammkunden darf nicht unter 30 % fallen.” Sie zielt weder auf einzelne Aufträge noch auf einzelne Kunden, sondern au"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die Engpassmaschine soll zu mindestens 92 % ausgelastet sein“","anker":"denkfehler-loesung-modellierung","text":"Fehler 1: Auslastung ist ein Ergebnis, keine Anforderung."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-modellierung","text":"1. b) Die Zielfunktion ist eine vollständige Aussage darüber, was zählt. Was nicht darin steht, ist für das Modell nicht „weniger wichtig”, sondern wertlos. (a) und (c) sind frei e"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-modellierung","text":""},{"seite":"anhang-loesungen.html","titel":"A.5 Lösungen zu Kapitel „Lineare Programmierung — Simplex, Dualität und Schattenpreise“","anker":"sec:loesungen-lp","text":"4.1 — Schlupf deuten. (a) Ressourcen 1 und 3 (Schlupf 0). (b) Ja: Überall ist s_i \\cdot y_i = 0. (c) In Ressource 3 — der höchste Schattenpreis (9,8) bedeutet den größten Grenznutz"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 4.1 — Die 380 000-Euro-Maschine","anker":"finde-den-denkfehler-4.1-die-380-000-euro-maschine","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-lp","text":"1 — (b). Genau der Satz vom komplementären Schlupf: s_i \\cdot y_i = 0. Ist Schlupf vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil verkehrt; (c) verwec"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-lp","text":""},{"seite":"anhang-loesungen.html","titel":"A.6 Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“","anker":"sec:loesungen-milp","text":"5.1 — Runden widerlegen. Beispiel: \\max x_1 + x_2 u. d. N. 10x_1 + 10x_2 \\le 15, ganzzahlig. LP-Optimum Z = 1{,}5; Abrunden ergibt (0,0) mit Z = 0 — 100 % Verlust. Das ganzzahlige "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 5.1 — Elf Lager, die keine Fixkosten kosten","anker":"finde-den-denkfehler-5.1-elf-lager-die-keine-fixkosten-kosten","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-milp","text":"1 — (b). Der Gap (48\\,200 - 47\\,100)/48\\,200 = 2{,}3\\,\\% ist eine Garantie, keine Schätzung: Besser als 47 100 € kann es nachweislich nicht werden, also ist der vorliegende Plan hö"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-milp","text":""},{"seite":"anhang-loesungen.html","titel":"A.7 Lösungen zu Kapitel „Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung“","anker":"sec:loesungen-cpsat","text":"6.1 — Propagation. Aus x_1 + x_2 = 8 und x_1 < x_2 folgt x_1 < 4, also x_1 \\in \\{2,3\\} (denn x_2 = 8-x_1 \\le 6 verlangt x_1 \\ge 2) und entsprechend x_2 \\in \\{5,6\\}. Aus 36 Ko"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 6.1 — Die Betriebsvereinbarung, die niemanden interessiert","anker":"finde-den-denkfehler-6.1-die-betriebsvereinbarung-die-niemanden-interessiert","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-cpsat","text":"1 — (b) wirkungslos. INFEASIBLE ist kein Abbruch, sondern ein Beweis: CP-SAT hat gezeigt, dass keine zulässige Lösung existiert. Mehr Zeit (a) oder mehr Arbeiter (c) ändern daran n"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-cpsat","text":""},{"seite":"anhang-loesungen.html","titel":"A.8 Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“","anker":"sec:loesungen-graphen","text":"7.1 — Flusserhaltung. b_i = (15+3) - (12+8) = -2 → Senke (Nettobedarf 2)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 7.1 — Die vergessene Dimension","anker":"finde-den-denkfehler-7.1-die-vergessene-dimension","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-graphen","text":"1 — (b) totale Unimodularität. Jede quadratische Teilmatrix hat Determinante 0, +1 oder -1; bei ganzzahliger rechter Seite sind deshalb alle Ecken des zulässigen Bereichs ganzzahli"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-graphen","text":""},{"seite":"anhang-loesungen.html","titel":"A.9 Lösungen zu Kapitel „Metaheuristiken — wenn der exakte Solver aussteigt“","anker":"sec:loesungen-metaheuristiken","text":"Die Kurzsichtigkeit von Hand. Ab Schwarz wählt die Faustregel: Schwarz → Dunkelrot (26) → Rot (5) → … und muss dann in die helle Gruppe: Rot → Weiß (50) → Elfenbein (2) → Beige (2)"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Unsere Heuristik ist 6 % besser“","anker":"denkfehler-loesung-metaheuristiken","text":"Es fehlt die Schranke."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-metaheuristiken","text":"1. b) Eine lokale Suche lebt von der Zahl geprüfter Züge. Die volle Summe kostet O(n) statt O(1) — bei 500 Aufträgen zwei Größenordnungen weniger Züge im selben Zeitbudget. Numeris"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-metaheuristiken","text":""},{"seite":"anhang-loesungen.html","titel":"A.10 Lösungen zu Kapitel „Spaltengenerierung“","anker":"sec:loesungen-dekomposition","text":"Das Abbruchkriterium. Die Eins ist der Zielfunktionskoeffizient eines Musters: Jedes geschnittene Muster verbraucht genau eine Mutterrolle, und die Zielfunktion lautet \\min \\sum_p "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die LP-Lösung sagt 72,92 — also runden wir auf“","anker":"denkfehler-loesung-dekomposition","text":"Das Aufrunden kostet sechs Rollen — acht Prozent."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-dekomposition","text":"1. b) Das Mustermodell kennt keine einzelnen Rollen und damit keine austauschbaren Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund; (c) "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-dekomposition","text":""},{"seite":"anhang-loesungen.html","titel":"A.11 Lösungen zu Kapitel „Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität“","anker":"sec:loesungen-qp-nlp","text":"8.1 — Konvexität einordnen. PSD (alle \\ge 0) → konvex, aber nicht streng konvex; die Lösung ist nicht notwendig eindeutig. Der Eigenwert 0 bedeutet eine flache Richtung: Entlang de"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 8.1 — Die Kovarianzmatrix aus dem Controlling","anker":"finde-den-denkfehler-8.1-die-kovarianzmatrix-aus-dem-controlling","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-qp-nlp","text":"1 — (b). Der DCPError ist eine inhaltliche Aussage: Für diese Formulierung kann es keine Optimalitätsgarantie geben. Auf scipy auszuweichen (a) beseitigt die Meldung, nicht die Urs"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-qp-nlp","text":""},{"seite":"anhang-loesungen.html","titel":"A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“","anker":"sec:loesungen-unsicherheit","text":"9.1 — Fluch des Durchschnitts. Weil sich Verzögerungen fortpflanzen und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (d"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 9.1 — Warum jedes Projekt zu spät fertig wird","anker":"finde-den-denkfehler-9.1-warum-jedes-projekt-zu-spät-fertig-wird","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-unsicherheit","text":"1 — (b). Das kritische Verhältnis 1400/(1400+120) = 0{,}921 gibt an, wie weit man sich auf die günstigere Fehlerseite stellen soll: Bestellt wird das 92,1-%-Quantil des Bedarfs, hi"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-unsicherheit","text":""},{"seite":"anhang-loesungen.html","titel":"A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“","anker":"sec:loesungen-dynamische-programmierung","text":"10.1 — Bausteine. Stufe = Tag 1–5; Zustand = verbleibende Distanz (ggf. plus Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung. Nicht in den "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 10.1 — Der Zustand, der zu wenig weiß","anker":"finde-den-denkfehler-10.1-der-zustand-der-zu-wenig-weiß","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-dynamische-programmierung","text":"1 — (b). V_t setzt V_{t+1} voraus: Eine Entscheidung lässt sich erst bewerten, wenn feststeht, was sie für die Zukunft bedeutet. Am Ende ist dieser Wert bekannt (V_T = g), deshalb "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-dynamische-programmierung","text":""},{"seite":"anhang-loesungen.html","titel":"A.14 Lösungen zu Kapitel „Mehrere Ziele — Pareto-Fronten statt Gewichte“","anker":"sec:loesungen-mehrziel","text":"Dominanz prüfen. P (14 000 €, 6 000 kg) und Q (13 800 €, 6 100 kg): keiner dominiert den anderen. Q ist billiger, P ist sauberer — beide sind pareto-optimal, die Wahl ist eine Präf"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir haben die Gewichte sauber kalibriert“","anker":"denkfehler-loesung-mehrziel","text":"Der Lösungsraum wurde nicht vollständig abgetastet — und zwar aus prinzipiellen Gründen."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-mehrziel","text":"1. b) Geometrisch heißt „gewichtete Summe minimieren“: eine Gerade von links unten an die Punktwolke schieben. Sie berührt immer einen Eckpunkt der unteren konvexen Hülle; Punkte d"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-mehrziel","text":""},{"seite":"anhang-loesungen.html","titel":"A.15 Lösungen zu Kapitel „Predict-then-Optimize“","anker":"sec:loesungen-prognose","text":"Andere Preise. Der Überhang kostet nur noch 1,50 €, die Fehlmenge weiterhin 6 €. Das kritische Verhältnis steigt von 6/9 = 0{,}667 auf 6/7{,}5 = 0{,}80. Die Bestellmenge verschiebt"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir haben die Prognose um 18 % verbessert“","anker":"denkfehler-loesung-prognose","text":"Der Sicherheitszuschlag stammt aus den Residuen des alten Modells."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-prognose","text":"1. b) Die Kostenasymmetrie verschiebt das Optimum vom Erwartungswert zum kritischen Quantil. (a) und (c) sind reale Probleme, aber nicht dieses: Selbst bei perfekt bekannter, norma"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-prognose","text":""},{"seite":"anhang-loesungen.html","titel":"A.16 Lösungen zu Kapitel „Die Strukturbrücke — dieselbe Mathematik, zwei Welten“","anker":"sec:loesungen-bruecke","text":"Die dritte Ressource. „Liquidität” — wie schnell sich eine Position ohne Kursabschlag verkaufen lässt — entspricht in der Werkstatt der Umrüstbarkeit oder der Vorlaufzeit: Wie schn"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Das ist doch dasselbe Problem“","anker":"denkfehler-loesung-bruecke","text":"Fehler 1: Varianz ist hier das falsche Risikomaß — genau der Fall, für den es CVaR gibt."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-bruecke","text":"1. b) Beide Probleme haben dieselbe Struktur: knappe Größen auf konkurrierende Verwendungen verteilen. (a) ist falsch — Produktionsproblem ist streng typisiert und validiert beim E"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-bruecke","text":""},{"seite":"anhang-loesungen.html","titel":"A.17 Lösungen zu Kapitel „Supply-Chain und Energieeinsatz unter Unsicherheit“","anker":"sec:loesungen-supplychain","text":"Der Umschlagpunkt. Bei 60 MW statt 120 MW halbiert sich die Grenzkostendifferenz je Stunde (83 €/MWh × 60 MW = 4 980 €/h). Der Umschlagpunkt verdoppelt sich auf 7,7 Stunden."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir rechnen mit dem P50-Szenario“","anker":"denkfehler-loesung-supplychain","text":"Fehler 1: Der Median ist keine Absicherung, sondern eine Wette auf die Hälfte der Fälle."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-supplychain","text":"1. b) Anfahrkosten und Mindestlaufzeiten koppeln die Stunden. Die Merit-Order ist eine Sortierung und kennt keine Kopplung über die Zeit; sie ist für einen einzelnen Zeitpunkt rich"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-supplychain","text":""},{"seite":"anhang-loesungen.html","titel":"A.18 Lösungen zu Kapitel „Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage“","anker":"sec:loesungen-finanzdaten","text":"11.1 — Renditeart. (a) diskret. (b) logarithmisch. (c) logarithmisch (bzw. diskret kumuliert über Produkt). (d) logarithmisch (statistische Eigenschaften)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 11.1 — Das risikofreie Portfolio","anker":"finde-den-denkfehler-11.1-das-risikofreie-portfolio","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-finanzdaten","text":"1 — (b) bei 75 %. 1{,}0 \\cdot 0{,}5 \\cdot 1{,}5 = 0{,}75. Prozentuale Änderungen verknüpfen sich multiplikativ; ein Verlust wiegt schwerer als ein gleich großer Gewinn, weil er von"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-finanzdaten","text":""},{"seite":"anhang-loesungen.html","titel":"A.19 Lösungen zu Kapitel „Die moderne Portfoliotheorie nach Markowitz“","anker":"sec:loesungen-markowitz","text":"12.1 — Diversifikationseffekt."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 12.1 — Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio","anker":"finde-den-denkfehler-12.1-zwölf-gleiche-anlagen-ein-sehr-ungleiches-portfolio","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-markowitz","text":"1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und bes"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-markowitz","text":""},{"seite":"anhang-loesungen.html","titel":"A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“","anker":"sec:loesungen-cvar","text":"13.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \\text{VaR}_{80\\%} = 2{"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 13.1 — Das Risikobudget, das durch Diversifikation stieg","anker":"finde-den-denkfehler-13.1-das-risikobudget-das-durch-diversifikation-stieg","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-cvar","text":"1 — (b) nichts. Der VaR ist ein Quantil: Er markiert die Schwelle und sagt nichts über den Bereich dahinter. Genau das zeigt der Schnellstart dieses Kapitels — zwei Anlagen mit ide"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-cvar","text":""},{"seite":"anhang-loesungen.html","titel":"A.21 Lösungen zu Kapitel „Die vollständige quantitative Handelsmaschine“","anker":"sec:loesungen-handelsmaschine","text":"14.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus D"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 14.1 — Die Strategie mit dem hochsignifikanten Ergebnis","anker":"finde-den-denkfehler-14.1-die-strategie-mit-dem-hochsignifikanten-ergebnis","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-handelsmaschine","text":"1 — (b) nichts. Beide Kennzahlen sind Mittelwerte über die Zeit und blind für die Reihenfolge. Der Schnellstart dieses Kapitels zeigt es an denselben Tagesrenditen, nur anders ange"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-handelsmaschine","text":""},{"seite":"anhang-loesungen.html","titel":"A.22 Lösungen zu Kapitel „Praxisfallen und der Weg zum produktiven Einsatz“","anker":"sec:loesungen-praxisfallen","text":"15.1 — Hart oder weich, revisited. (a) hart: gesetzliche Ruhezeit, Qualifikationspflicht. (b) weich: individuelle Wunschtage, Vermeidung von Freistunden. (c) diskutabel: Höchstzahl"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 15.1 — Das Modell, das seit einem Jahr nicht mehr optimiert","anker":"finde-den-denkfehler-15.1-das-modell-das-seit-einem-jahr-nicht-mehr-optimiert","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-praxisfallen","text":"1 — (b) das Zeitlimit greift. Eine Laufzeit, die exakt dem Limit entspricht und über Monate konstant bleibt, ist kein Stabilitätsbeleg, sondern zeigt, dass der Solver jedes Mal abg"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-praxisfallen","text":""},{"seite":"anhang-loesungen.html","titel":"A.23 Lösungen zu Kapitel „Testen, Messen, Ausliefern“","anker":"sec:loesungen-testing","text":"Den Schnellstart reparieren. Zwei Wege: (a) Abrunden statt runden — np.floor statt np.round. Das ist immer zulässig, weil weniger produzieren nie eine Kapazität sprengt, kostet abe"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die Suite ist grün, das Modell stimmt”","anker":"denkfehler-loesung-testing","text":"Alle Prüfungen benutzen dieselbe falsche Zahl."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-testing","text":"1. b) Es gibt keine unabhängige Quelle für die richtige Antwort — sonst bräuchte man den Solver nicht. (a) ist bei festgelegtem Seed und Zeitlimit meist beherrschbar; (c) löst man "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-testing","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Anhang B: Katalog der Modellierungsmuster","anker":null,"text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Übersicht","anker":"übersicht","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Logische Schalter","anker":"logische-schalter","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B1 — Aktivierungsschalter (Fixkosten)","anker":"b1-aktivierungsschalter-fixkosten","text":"Regel. „Wenn überhaupt etwas produziert wird, fallen Rüstkosten F an.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B2 — Semikontinuierliche Variable","anker":"b2-semikontinuierliche-variable","text":"Regel. „Entweder gar nicht oder mindestens L (und höchstens U).“"},{"seite":"anhang-modellierungsmuster.html","titel":"B3 — Implikation","anker":"b3-implikation","text":"Regel. „Wenn A gewählt wird, muss auch B gewählt werden.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B4 — Entweder-Oder (disjunktive Bedingung)","anker":"b4-entweder-oder-disjunktive-bedingung","text":"Regel. „Es muss f(\\mathbf{x}) \\le b_1 oder g(\\mathbf{x}) \\le b_2 gelten.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B5 — Exklusiv-Oder","anker":"b5-exklusiv-oder","text":"Regel. „Genau eine der Optionen wird gewählt.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B6 — Kardinalität","anker":"b6-kardinalität","text":"Regel. „Höchstens (mindestens, genau) K von N.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B7 — Bedingte Kopplung (Konjunktion)","anker":"b7-bedingte-kopplung-konjunktion","text":"Regel. „Wenn A und B, dann auch C.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Mengen und Grenzen","anker":"mengen-und-grenzen","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B8 — Weiche Grenze mit Strafkosten","anker":"b8-weiche-grenze-mit-strafkosten","text":"Regel. „Möglichst nicht über b — wenn doch, kostet es.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B9 — Gestaffelte Preise (stückweise linear)","anker":"b9-gestaffelte-preise-stückweise-linear","text":"Regel. „Die ersten 100 Stück kosten 5 €, danach 8 €.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B10 — Absolutbetrag / Abweichung","anker":"b10-absolutbetrag-abweichung","text":"Regel. „Die Abweichung vom Zielwert soll klein sein, egal in welche Richtung.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B11 — Min/Max in der Zielfunktion","anker":"b11-minmax-in-der-zielfunktion","text":"Regel. „Der am schlechtesten gestellte Beteiligte soll möglichst gut dastehen“ (Maximin/Fairness)."},{"seite":"anhang-modellierungsmuster.html","titel":"B12 — Verhältnis-Bedingung","anker":"b12-verhältnis-bedingung","text":"Regel. „Der Anteil von Gruppe G soll mindestens 30 % betragen.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B25 — Mindestabnahmemenge über einen Zeitraum","anker":"b25-mindestabnahmemenge-über-einen-zeitraum","text":"Regel. „Entweder wir arbeiten mit diesem Lieferanten gar nicht, oder wir nehmen ihm im Jahr mindestens 500 Einheiten ab.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B27 — Budgetlimit","anker":"b27-budgetlimit","text":"Regel. „Alle Maßnahmen zusammen dürfen 2 Mio. € nicht überschreiten.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Zeit und Reihenfolge","anker":"zeit-und-reihenfolge","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B13 — Vorrangbeziehung","anker":"b13-vorrangbeziehung","text":"Regel. „Arbeitsgang B darf erst beginnen, wenn A fertig ist.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B14 — Nichtüberlappung","anker":"b14-nichtüberlappung","text":"Regel. „Eine Maschine bearbeitet nur einen Job gleichzeitig.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B15 — Kumulative Ressource","anker":"b15-kumulative-ressource","text":"Regel. „Zu keinem Zeitpunkt dürfen mehr als 3 Arbeiten gleichzeitig laufen“ bzw. „die Stromlast darf 500 kW nie übersteigen“."},{"seite":"anhang-modellierungsmuster.html","titel":"B16 — Gleitendes Fenster","anker":"b16-gleitendes-fenster","text":"Regel. „Höchstens 5 Arbeitstage in Folge“ / „mindestens 2 freie Tage je Woche“."},{"seite":"anhang-modellierungsmuster.html","titel":"B17 — Umrüst- bzw. Wechselkosten","anker":"b17-umrüst--bzw.-wechselkosten","text":"Regel. „Ein Produktwechsel kostet Rüstzeit.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B26 — Rüstzeit als Kapazitätsverbrauch","anker":"b26-rüstzeit-als-kapazitätsverbrauch","text":"Regel. „Das Umrüsten kostet nicht nur Geld, es kostet Maschinenstunden — und die fehlen dann für die Produktion.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Robustheit und Diagnose","anker":"robustheit-und-diagnose","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B18 — Schlupfvariablen gegen Unlösbarkeit","anker":"b18-schlupfvariablen-gegen-unlösbarkeit","text":"Das wichtigste Muster für den Produktivbetrieb."},{"seite":"anhang-modellierungsmuster.html","titel":"B19 — Hierarchische Ziele (lexikografisch)","anker":"b19-hierarchische-ziele-lexikografisch","text":"Regel. „Erst die Besetzung sicherstellen, dann die Fairness optimieren.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B20 — Symmetriebrechung","anker":"b20-symmetriebrechung","text":"Regel. „Drei identische Maschinen — der Solver soll nicht alle Vertauschungen durchprobieren.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B21 — Worst-Case-Abzug (robuste Formulierung)","anker":"b21-worst-case-abzug-robuste-formulierung","text":"Regel. „Rechne nicht mit dem geschätzten Wert, sondern mit dem ungünstigsten plausiblen.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Netzwerke","anker":"netzwerke","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B22 — Flusserhaltung","anker":"b22-flusserhaltung","text":"\\sum_{j:(i,j)\\in E} x_{ij} - \\sum_{k:(k,i)\\in E} x_{ki} = b_i \\qquad \\forall i"},{"seite":"anhang-modellierungsmuster.html","titel":"B23 — Zuordnung 1:1","anker":"b23-zuordnung-11","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B24 — Subtour-Eliminierung","anker":"b24-subtour-eliminierung","text":"MTZ (einfach, aber schwach): u_i - u_j + C x_{ij} \\le C - d_j \\qquad \\forall i\\ne j"},{"seite":"anhang-modellierungsmuster.html","titel":"Ein Wort zur Auswahl","anker":"ein-wort-zur-auswahl","text":"Wenn mehrere Muster passen, entscheiden Sie nach dieser Reihenfolge:"},{"seite":"anhang-fehlerdiagnose.html","titel":"Anhang C: Fehlerdiagnose-Handbuch","anker":null,"text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Symptom-Schnellübersicht","anker":"symptom-schnellübersicht","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C1 — INFEASIBLE","anker":"c1-infeasible","text":"Bedeutung. Es gibt keinen einzigen Punkt, der alle Bedingungen gleichzeitig erfüllt. Das ist eine Aussage über Ihr Modell, nicht über den Solver."},{"seite":"anhang-fehlerdiagnose.html","titel":"Diagnose in fünf Schritten","anker":"diagnose-in-fünf-schritten","text":"Schritt 1 — Trivialprüfungen von Hand. Rechnen Sie die offensichtlichen Bilanzen nach, bevor Sie den Solver befragen:"},{"seite":"anhang-fehlerdiagnose.html","titel":"Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen","anker":"wenn-die-fünf-schritte-nicht-reichen-den-konflikt-einkreisen","text":"Warum Schritt 2 so oft ins Leere läuft. Bedingungen einzeln abzuschalten funktioniert nur, solange es einen Schuldigen gibt. Überlagern sich zwei unabhängige Widersprüche, bleibt d"},{"seite":"anhang-fehlerdiagnose.html","titel":"C2 — UNBOUNDED","anker":"c2-unbounded","text":"Bedeutung. Die Zielfunktion lässt sich unbegrenzt verbessern."},{"seite":"anhang-fehlerdiagnose.html","titel":"C3 — Zu langsam","anker":"c3-zu-langsam","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Diagnosereihenfolge","anker":"diagnosereihenfolge","text":"1. Ist es überhaupt das Lösen?"},{"seite":"anhang-fehlerdiagnose.html","titel":"C4 — Unsinniges Ergebnis","anker":"c4-unsinniges-ergebnis","text":"Der Solver hat immer recht — bezogen auf das Modell, das Sie ihm gegeben haben. Wenn das Ergebnis unsinnig ist, beschreibt Ihr Modell nicht das Problem, das Sie meinen."},{"seite":"anhang-fehlerdiagnose.html","titel":"Checkliste","anker":"checkliste","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Die wirksamste Gegenmaßnahme","anker":"die-wirksamste-gegenmaßnahme","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C5 — Instabile Lösung","anker":"c5-instabile-lösung","text":"Symptom. Ein zusätzlicher Handelstag, und das Portfolio sieht völlig anders aus."},{"seite":"anhang-fehlerdiagnose.html","titel":"Derselbe Fehler außerhalb der Finanzwelt","anker":"derselbe-fehler-außerhalb-der-finanzwelt","text":"Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^"},{"seite":"anhang-fehlerdiagnose.html","titel":"C6 — Falsche Dualwerte","anker":"c6-falsche-dualwerte","text":"Symptom. Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource."},{"seite":"anhang-fehlerdiagnose.html","titel":"Die drei Prüfungen","anker":"die-drei-prüfungen","text":"1. Vorzeichenkonvention. Haben Sie zur Maximierung negiert?"},{"seite":"anhang-fehlerdiagnose.html","titel":"C7 — Widersprüchliche Solver","anker":"c7-widersprüchliche-solver","text":"Symptom. linprog sagt 530, CVXPY sagt 529,8."},{"seite":"anhang-fehlerdiagnose.html","titel":"C8 — DCPError","anker":"c8-dcperror","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C9 — Importfehler","anker":"c9-importfehler","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C10 — Verdächtig guter Backtest","anker":"c10-verdächtig-guter-backtest","text":"Faustregel: Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund."},{"seite":"anhang-fehlerdiagnose.html","titel":"Prüfreihenfolge","anker":"prüfreihenfolge","text":"1. Lookahead-Selbsttest."},{"seite":"anhang-fehlerdiagnose.html","titel":"C11 — Vertauschte Spalten","anker":"c11-vertauschte-spalten","text":"Symptom. Ergebnisse sind plausibel, aber falsch beschriftet — oder eine Sektorgrenze greift auf die falschen Titel."},{"seite":"anhang-fehlerdiagnose.html","titel":"Die goldene Diagnoseregel","anker":"die-goldene-diagnoseregel","text":""},{"seite":"anhang-spickzettel.html","titel":"Anhang D: Spickzettel der Solver","anker":null,"text":""},{"seite":"anhang-spickzettel.html","titel":"D1 — SciPy: linprog und milp","anker":"d1-scipy-linprog-und-milp","text":"Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nic"},{"seite":"anhang-spickzettel.html","titel":"Lineares Programm","anker":"lineares-programm","text":""},{"seite":"anhang-spickzettel.html","titel":"Ganzzahlig: milp","anker":"ganzzahlig-milp","text":""},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine","text":""},{"seite":"anhang-spickzettel.html","titel":"D2 — HiGHS über highspy","anker":"d2-highs-über-highspy","text":"Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-1","text":""},{"seite":"anhang-spickzettel.html","titel":"D3 — OR-Tools: pywraplp","anker":"d3-or-tools-pywraplp","text":"Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht: Scheduling und kombinatorische Bedingungen — dafür ist CP-SA"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-2","text":""},{"seite":"anhang-spickzettel.html","titel":"D4 — CP-SAT: cp_model","anker":"d4-cp-sat-cp_model","text":"Wofür. Scheduling, Zuordnung, Reihenfolgen, alles Kombinatorische mit globalen Bedingungen. Wofür nicht: stetige Größen — CP-SAT rechnet ausschließlich ganzzahlig. Wer Nachkommaste"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-3","text":""},{"seite":"anhang-spickzettel.html","titel":"D5 — CVXPY","anker":"d5-cvxpy","text":"Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst "},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-4","text":""},{"seite":"anhang-spickzettel.html","titel":"D6 — Modellierungssprachen: Pyomo und Linopy","anker":"d6-modellierungssprachen-pyomo-und-linopy","text":"Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy "},{"seite":"anhang-spickzettel.html","titel":"Pyomo","anker":"pyomo","text":""},{"seite":"anhang-spickzettel.html","titel":"Linopy","anker":"linopy","text":""},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-5","text":""},{"seite":"anhang-spickzettel.html","titel":"Die gemeinsame Regel","anker":"die-gemeinsame-regel","text":"Alle sechs Seiten haben denselben längsten Abschnitt: die Statusauswertung. Das ist kein Zufall. Ein Solveraufruf hat nie zwei Ausgänge, sondern mindestens fünf — optimal, zulässig"},{"seite":"anhang-glossar-literatur.html","titel":"Anhang E: Glossar und Literatur","anker":null,"text":""},{"seite":"anhang-glossar-literatur.html","titel":"Mathematische Optimierung — Grundlagen","anker":"mathematische-optimierung-grundlagen","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Konvexe, nichtlineare und robuste Optimierung","anker":"konvexe-nichtlineare-und-robuste-optimierung","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Constraint Programming und Scheduling","anker":"constraint-programming-und-scheduling","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Quantitative Finanzmathematik","anker":"quantitative-finanzmathematik","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Software und Dokumentation","anker":"software-und-dokumentation","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Verbände und Normen","anker":"verbände-und-normen","text":""}]; +window.OR_SEARCH_INDEX = [{"seite":"vorwort.html","titel":"Vorwort & Lesehilfe","anker":null,"text":""},{"seite":"notation.html","titel":"Notation & Abkürzungen","anker":null,"text":""},{"seite":"notation.html","titel":"1. Die Symbole der Optimierung","anker":"die-symbole-der-optimierung","text":""},{"seite":"notation.html","titel":"1.1 Grundgrößen jedes Modells","anker":"grundgrößen-jedes-modells","text":""},{"seite":"notation.html","titel":"1.2 Mengen und Zahlbereiche","anker":"mengen-und-zahlbereiche","text":""},{"seite":"notation.html","titel":"1.3 Operatoren und Schreibweisen","anker":"operatoren-und-schreibweisen","text":""},{"seite":"notation.html","titel":"1.4 Symbole der Finanzkapitel (Teil IV)","anker":"symbole-der-finanzkapitel-teil-iv","text":""},{"seite":"notation.html","titel":"1.5 Symbole der Verfahrenskapitel (Teil II und III)","anker":"symbole-der-verfahrenskapitel-teil-ii-und-iii","text":"Diese Zeichen kommen erst in den Kapiteln vor, die über das klassische LP hinausgehen."},{"seite":"notation.html","titel":"2. Abkürzungen — vollständig ausgeschrieben","anker":"abkürzungen-vollständig-ausgeschrieben","text":""},{"seite":"notation.html","titel":"2.1 Problemklassen und Verfahren","anker":"problemklassen-und-verfahren","text":""},{"seite":"notation.html","titel":"2.2 Probleme mit Eigennamen","anker":"probleme-mit-eigennamen","text":""},{"seite":"notation.html","titel":"2.3 Finanz- und Risikokennzahlen","anker":"finanz--und-risikokennzahlen","text":""},{"seite":"notation.html","titel":"2.4 Software, Solver und Schnittstellen","anker":"software-solver-und-schnittstellen","text":""},{"seite":"notation.html","titel":"2.5 Begriffe aus dem Solver-Alltag","anker":"begriffe-aus-dem-solver-alltag","text":""},{"seite":"notation.html","titel":"3. Konventionen in diesem Buch","anker":"konventionen-in-diesem-buch","text":"Vektoren sind grundsätzlich Spaltenvektoren und werden fett gesetzt: \\mathbf{x}. Ein transponierter Vektor \\mathbf{x}^\\top ist eine Zeile. Skalare bleiben mager: n, Z."},{"seite":"einfuehrung.html","titel":"Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung","anker":null,"text":""},{"seite":"einfuehrung.html","titel":"1.1 In 5 Minuten gelöst","anker":"sec:einfuehrung-schnellstart","text":"Bevor wir über Theorie sprechen, lösen Sie ein echtes Problem. Kopieren Sie den folgenden Block, führen Sie ihn aus, sehen Sie das Ergebnis — die Erklärung kommt danach."},{"seite":"einfuehrung.html","titel":"1.2 Lernziele","anker":"sec:einfuehrung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"einfuehrung.html","titel":"1.3 Was ist Operations Research wirklich?","anker":"sec:einfuehrung-was-ist-operations-research-wirklich","text":"Im Alltag lösen wir Probleme intuitiv: durch Ausprobieren, Daumenregeln, Erfahrung. Das funktioniert erstaunlich gut — bis eine bestimmte Schwelle überschritten wird. Dann bricht d"},{"seite":"einfuehrung.html","titel":"Die drei Stufen der Analytik","anker":"die-drei-stufen-der-analytik","text":"OR beantwortet nicht die beschreibende Frage „Was ist passiert?“ und nicht die vorhersagende Frage „Was wird passieren?“, sondern die vorschreibende (präskriptive) Kernfrage:"},{"seite":"einfuehrung.html","titel":"1.4 Warum Ausprobieren scheitert — mit eigener Rechnung","anker":"sec:einfuehrung-warum-ausprobieren-scheitert-mit-eigener-rechnung","text":"Die Behauptung „vollständiges Durchprobieren ist unmöglich“ wird oft aufgestellt und selten belegt. Rechnen wir sie einmal selbst nach — das Ergebnis prägt sich besser ein als jede"},{"seite":"einfuehrung.html","titel":"1.5 Historischer Kontext und Evolution","anker":"sec:einfuehrung-historischer-kontext-und-evolution","text":"Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man"},{"seite":"einfuehrung.html","titel":"1.6 Die vier universellen Bausteine jedes OR-Problems","anker":"sec:einfuehrung-die-vier-universellen-bausteine-jedes-or","text":"Dies ist der wichtigste Abschnitt des Kapitels. Jedes Optimierungsproblem der Welt lässt sich — unabhängig von der Branche — auf vier Elemente reduzieren. Wenn Sie später vor einem"},{"seite":"einfuehrung.html","titel":"Baustein 1 — Entscheidungsvariablen (x)","anker":"baustein-1-entscheidungsvariablen-x","text":"Die Stellschrauben des Modells: Werte, die der Algorithmus aktiv festlegen darf."},{"seite":"einfuehrung.html","titel":"Baustein 2 — Parameter / Eingabedaten (c, A, b)","anker":"baustein-2-parameter-eingabedaten-c-a-b","text":"Die unveränderlichen Fakten der Realität: historische Renditen, Maschinenstundensätze, Lagerkapazitäten, Steuerquoten, Entfernungen, Qualifikationen."},{"seite":"einfuehrung.html","titel":"Baustein 3 — Zielfunktion (f(x))","anker":"baustein-3-zielfunktion-fx","text":"Ein mathematischer Ausdruck, der die Güte einer Lösung zu einer einzigen Zahl verdichtet:"},{"seite":"einfuehrung.html","titel":"Baustein 4 — Nebenbedingungen (Constraints)","anker":"baustein-4-nebenbedingungen-constraints","text":"Regeln, die den Raum der erlaubten Lösungen einschränken:"},{"seite":"einfuehrung.html","titel":"Die vier Bausteine als eine Formel","anker":"die-vier-bausteine-als-eine-formel","text":"Setzt man die vier Bausteine zusammen, entsteht die Standardform, in der Ihnen jedes Optimierungsmodell dieses Buches begegnen wird. Sie sieht auf den ersten Blick sperrig aus — de"},{"seite":"einfuehrung.html","titel":"Die Bausteine in der Praxis: eine Vorlage","anker":"die-bausteine-in-der-praxis-eine-vorlage","text":"Damit die Zerlegung zur Gewohnheit wird, hier eine Vorlage, die Sie bei jedem neuen Problem ausfüllen können — vor der ersten Codezeile."},{"seite":"einfuehrung.html","titel":"1.7 Erstes Python-Vollbeispiel: Ressourcenallokation im Rechenzentrum","anker":"sec:einfuehrung-erstes-python-vollbeispiel-ressourcenallokation-im-rechenzentrum","text":""},{"seite":"einfuehrung.html","titel":"Szenario","anker":"szenario","text":"Ein quantitatives Handelshaus betreibt Serverkapazitäten für zwei Algorithmentypen:"},{"seite":"einfuehrung.html","titel":"Mathematische Formulierung","anker":"mathematische-formulierung","text":"Entscheidungsvariablen:"},{"seite":"einfuehrung.html","titel":"Umsetzung mit Google OR-Tools","anker":"umsetzung-mit-google-or-tools","text":""},{"seite":"einfuehrung.html","titel":"1.8 Von Excel zu Python: Ihre Daten liegen schon da","anker":"sec:einfuehrung-excel-bruecke","text":"In den allermeisten Betrieben liegen die Zahlen, die ein Optimierungsmodell braucht, nicht in einer Datenbank, sondern in einer Tabellenkalkulation. Und viele Leserinnen und Leser "},{"seite":"einfuehrung.html","titel":"1.9 Übungsaufgaben","anker":"sec:einfuehrung-uebungsaufgaben","text":""},{"seite":"einfuehrung.html","titel":"1.10 Finde den Denkfehler","anker":"sec:einfuehrung-denkfehler","text":"Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht stimmt. Gefährlich sind Programme, die fehlerfrei durchlaufen, einen Status OPTIMAL melden und eine Zah"},{"seite":"einfuehrung.html","titel":"1.11 Micro-Quiz","anker":"sec:einfuehrung-quiz","text":""},{"seite":"einfuehrung.html","titel":"1.12 Selbsttest","anker":"sec:einfuehrung-selbsttest","text":""},{"seite":"einfuehrung.html","titel":"1.13 Zusammenfassung","anker":"sec:einfuehrung-zusammenfassung","text":""},{"seite":"fundament.html","titel":"Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität","anker":null,"text":""},{"seite":"fundament.html","titel":"2.1 In 5 Minuten gelöst","anker":"sec:fundament-schnellstart","text":"Dieses Kapitel handelt von Geometrie. Bevor wir Vektoren und Matrizen einführen, sehen Sie die zentrale Einsicht in Aktion — ganz ohne Solver, mit acht Zeilen NumPy."},{"seite":"fundament.html","titel":"2.2 Lernziele","anker":"sec:fundament-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"fundament.html","titel":"2.3 Warum überhaupt Vektoren und Matrizen?","anker":"sec:fundament-warum-ueberhaupt-vektoren-und-matrizen","text":"In Kapitel 1 hatten wir zwei Variablen und drei Nebenbedingungen. Das ließ sich bequem ausschreiben. Reale Modelle haben hunderte bis hunderttausende Variablen — dort wäre Ausschre"},{"seite":"fundament.html","titel":"Die Bausteine","anker":"die-bausteine","text":"Sei n die Anzahl der Entscheidungsvariablen und m die Anzahl der Nebenbedingungen."},{"seite":"fundament.html","titel":"Die kanonische Standardform","anker":"die-kanonische-standardform","text":"Damit lässt sich jedes lineare Programm in drei Zeilen schreiben:"},{"seite":"fundament.html","titel":"Dasselbe in Python","anker":"dasselbe-in-python","text":""},{"seite":"fundament.html","titel":"2.4 Der zulässige Lösungsraum und das Polyeder","anker":"sec:fundament-der-zulaessige-loesungsraum-und-das-polyeder","text":"Jede lineare Ungleichung a_{i1}x_1 + a_{i2}x_2 \\le b_i definiert geometrisch eine Halbebene (im \\mathbb{R}^2) bzw. einen Halbraum (im \\mathbb{R}^n): Die zugehörige Gleichung ist ei"},{"seite":"fundament.html","titel":"Der Fundamentalsatz der linearen Optimierung","anker":"der-fundamentalsatz-der-linearen-optimierung","text":""},{"seite":"fundament.html","titel":"2.5 Konvexität: die Grenze zwischen leicht und schwer","anker":"sec:fundament-konvexitaet-die-grenze-zwischen-leicht-und","text":"Hier kommt die vielleicht wichtigste Einsicht des ganzen Buches. In der Optimierungstheorie verläuft die Trennlinie zwischen „zuverlässig lösbar“ und „im Allgemeinen hoffnungslos“ "},{"seite":"fundament.html","titel":"Konvexe Menge","anker":"konvexe-menge","text":"Eine Menge \\mathcal{C} \\subseteq \\mathbb{R}^n heißt konvex, wenn für alle Punkte \\mathbf{x}, \\mathbf{y} \\in \\mathcal{C} und jedes \\theta \\in [0,1] gilt:"},{"seite":"fundament.html","titel":"Konvexe Funktion","anker":"konvexe-funktion","text":"Eine Funktion f: \\mathcal{C} \\to \\mathbb{R} heißt konvex, wenn für alle \\mathbf{x}, \\mathbf{y} \\in \\mathcal{C} und \\theta \\in [0,1] gilt:"},{"seite":"fundament.html","titel":"Der zentrale Satz","anker":"der-zentrale-satz","text":""},{"seite":"fundament.html","titel":"Was ist konvex, was nicht?","anker":"was-ist-konvex-was-nicht","text":""},{"seite":"fundament.html","titel":"Konvexität sichtbar machen","anker":"konvexität-sichtbar-machen","text":""},{"seite":"fundament.html","titel":"2.6 Geometrische Visualisierung des Lösungsraums","anker":"sec:fundament-geometrische-visualisierung-des-loesungsraums","text":"Wir zeichnen nun den Lösungsraum aus Kapitel 1 und lesen das Optimum ab. Zweidimensionale Bilder sind der schnellste Weg, Intuition für höhere Dimensionen aufzubauen — auch wenn ma"},{"seite":"fundament.html","titel":"2.7 Wenn die Zahlen nicht zusammenpassen: Kondition und Skalierung","anker":"sec:fundament-kondition","text":"Bis hierher war die lineare Algebra exakt. Ein Rechner rechnet aber nicht exakt, sondern mit rund 16 signifikanten Stellen — und Ihre Betriebsdaten sind ohnehin nur auf wenige Stel"},{"seite":"fundament.html","titel":"Die geometrische Vorstellung: zwei fast parallele Linien","anker":"die-geometrische-vorstellung-zwei-fast-parallele-linien","text":"Zwei Nebenbedingungen legen im Zweidimensionalen einen Schnittpunkt fest. Stehen die zugehörigen Geraden kreuzweise aufeinander, ist der Schnittpunkt robust: Verschiebt man eine Ge"},{"seite":"fundament.html","titel":"Woher schlechte Kondition im Alltag kommt","anker":"woher-schlechte-kondition-im-alltag-kommt","text":"Fast nie aus exotischer Mathematik — fast immer aus Einheiten:"},{"seite":"fundament.html","titel":"Ruiz-Equilibrierung: das Modell gesundrechnen","anker":"ruiz-equilibrierung-das-modell-gesundrechnen","text":"Die Gegenmaßnahme ist erstaunlich schlicht. Man multipliziert jede Zeile und jede Spalte der Matrix mit einem Faktor, sodass die Beträge überall in derselben Größenordnung landen:"},{"seite":"fundament.html","titel":"Das Experiment","anker":"das-experiment","text":"Das folgende Programm führt beides vor — die Fehlerverstärkung und ihre Behebung — und schließt mit der praktischen Konsequenz, die Sie in Kapitel 6 wieder brauchen werden: warum e"},{"seite":"fundament.html","titel":"2.8 Übungsaufgaben","anker":"sec:fundament-uebungsaufgaben","text":""},{"seite":"fundament.html","titel":"2.9 Finde den Denkfehler","anker":"sec:fundament-denkfehler","text":""},{"seite":"fundament.html","titel":"2.10 Micro-Quiz","anker":"sec:fundament-quiz","text":""},{"seite":"fundament.html","titel":"2.11 Selbsttest","anker":"sec:fundament-selbsttest","text":""},{"seite":"fundament.html","titel":"2.12 Zusammenfassung","anker":"sec:fundament-zusammenfassung","text":""},{"seite":"oekosystem.html","titel":"Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten","anker":null,"text":""},{"seite":"oekosystem.html","titel":"3.1 In 5 Minuten gelöst","anker":"sec:oekosystem-schnellstart","text":""},{"seite":"oekosystem.html","titel":"3.2 Lernziele","anker":"sec:oekosystem-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"oekosystem.html","titel":"3.3 Die Zwei-Schichten-Architektur","anker":"sec:oekosystem-die-zwei-schichten-architektur","text":"Im modernen Operations Research schreibt man Optimierungsalgorithmen nicht selbst. Man nutzt eine zweischichtige Architektur:"},{"seite":"oekosystem.html","titel":"3.4 Die Werkzeuge im Vergleich","anker":"sec:oekosystem-die-werkzeuge-im-vergleich","text":""},{"seite":"oekosystem.html","titel":"Die Entscheidung in drei Fragen","anker":"die-entscheidung-in-drei-fragen","text":"Statt die Tabelle auswendig zu lernen, beantworten Sie drei Fragen:"},{"seite":"oekosystem.html","titel":"3.5 Ein System — vier Programmieransätze","anker":"sec:oekosystem-ein-system-vier-programmieransaetze","text":"Nichts macht die Unterschiede so deutlich wie dasselbe Problem, viermal gelöst. Wir nehmen:"},{"seite":"oekosystem.html","titel":"Was das CSR-Format bedeutet","anker":"was-das-csr-format-bedeutet","text":"highspy erwartet die Nebenbedingungsmatrix im CSR-Format (Compressed Sparse Row, komprimierte Zeilendarstellung). Statt der vollen Matrix speichert man nur die Einträge ungleich nu"},{"seite":"oekosystem.html","titel":"3.6 Wann lohnt sich welche Ebene?","anker":"sec:oekosystem-wann-lohnt-sich-welche-ebene","text":""},{"seite":"oekosystem.html","titel":"3.7 Modellierungsschichten für große Modelle: Pyomo und Linopy","anker":"sec:oekosystem-pyomo-linopy","text":"Die vier Bibliotheken aus dem Vierfach-Vergleich decken den Alltag weitgehend ab. Sobald Modelle industrielle Größe erreichen — zehntausende Variablen, Daten aus mehreren Systemen,"},{"seite":"oekosystem.html","titel":"Pyomo: die algebraische Denkweise","anker":"pyomo-die-algebraische-denkweise","text":"Pyomo ist im deutschsprachigen Raum der De-facto-Standard für große LP- und MILP-Modelle in Energiewirtschaft, Chemie und Logistik. Sein Kennzeichen: Es denkt in Mengen und Indizes"},{"seite":"oekosystem.html","titel":"Linopy: eine Zeile, zehntausend Nebenbedingungen","anker":"linopy-eine-zeile-zehntausend-nebenbedingungen","text":"Linopy verfolgt einen anderen Ansatz: Variablen sind beschriftete Arrays (xarray), keine indizierten Einzelobjekte. Damit wird aus"},{"seite":"oekosystem.html","titel":"Die Schichten im Überblick","anker":"die-schichten-im-überblick","text":""},{"seite":"oekosystem.html","titel":"3.8 Wo die Zeit wirklich hingeht: vektorisierte Modellgenerierung","anker":"sec:oekosystem-vektorisierung","text":"Eine der hartnäckigsten Fehlannahmen in Optimierungsprojekten lautet: „Wenn es zu langsam ist, brauchen wir einen besseren Solver.“ Messen Sie erst — oft stimmt das nicht."},{"seite":"oekosystem.html","titel":"Vier Stufen an einem Transportproblem","anker":"vier-stufen-an-einem-transportproblem","text":"Wir bauen dasselbe Transportproblem (m Werke, n Kunden, m \\cdot n Variablen) auf vier Arten auf:"},{"seite":"oekosystem.html","titel":"3.9 Übungsaufgaben","anker":"sec:oekosystem-uebungsaufgaben","text":""},{"seite":"oekosystem.html","titel":"3.10 Finde den Denkfehler","anker":"sec:oekosystem-denkfehler","text":""},{"seite":"oekosystem.html","titel":"3.11 Micro-Quiz","anker":"sec:oekosystem-quiz","text":""},{"seite":"oekosystem.html","titel":"3.12 Selbsttest","anker":"sec:oekosystem-selbsttest","text":""},{"seite":"oekosystem.html","titel":"3.13 Zusammenfassung","anker":"sec:oekosystem-zusammenfassung","text":""},{"seite":"modellierung.html","titel":"Kapitel 4: Vom Management-Wunsch zum Modell","anker":null,"text":""},{"seite":"modellierung.html","titel":"4.1 In 5 Minuten gelöst","anker":"sec:modellierung-schnellstart","text":""},{"seite":"modellierung.html","titel":"4.2 Lernziele","anker":"sec:modellierung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"modellierung.html","titel":"4.3 Ein Satz ist noch kein Modell","anker":"sec:modellierung-vier-fragen","text":"Was aus einer Besprechung mitkommt, klingt meist so:"},{"seite":"modellierung.html","titel":"4.4 Derselbe Datensatz, fünf Modelle","anker":"sec:modellierung-fuenf-modelle","text":"Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14 Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300."},{"seite":"modellierung.html","titel":"Teil 1: Drei Ziele, drei Pläne","anker":"teil-1-drei-ziele-drei-pläne","text":""},{"seite":"modellierung.html","titel":"Teil 2: Ein Satz, drei Lesarten","anker":"teil-2-ein-satz-drei-lesarten","text":"„Stammkunden dürfen wir nicht verlieren.” Jeder im Raum nickt. Niemand merkt, dass drei verschiedene Modelle gemeint sein können:"},{"seite":"modellierung.html","titel":"Teil 3: Hart oder weich","anker":"teil-3-hart-oder-weich","text":"Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:"},{"seite":"modellierung.html","titel":"Teil 4: Ein Quartal später","anker":"teil-4-ein-quartal-später","text":"Die harte Lesart A wurde eingebaut, alles lief drei Monate lang. Dann fragt Weber einen Auftrag über 320 Maschinenstunden an — mehr, als der ganze Zeitraum hergibt."},{"seite":"modellierung.html","titel":"4.5 Der Gesprächsleitfaden","anker":"sec:modellierung-leitfaden","text":"Was aus den vier Teilen als Handwerkszeug bleibt — Fragen, die man in der ersten Besprechung stellt, bevor eine Zeile Code entsteht:"},{"seite":"modellierung.html","titel":"4.6 Übungsaufgaben","anker":"sec:modellierung-uebungsaufgaben","text":""},{"seite":"modellierung.html","titel":"4.7 Finde den Denkfehler","anker":"sec:modellierung-denkfehler","text":""},{"seite":"modellierung.html","titel":"4.8 Micro-Quiz","anker":"sec:modellierung-quiz","text":""},{"seite":"modellierung.html","titel":"4.9 Selbsttest","anker":"sec:modellierung-selbsttest","text":""},{"seite":"modellierung.html","titel":"4.10 Zusammenfassung","anker":"sec:modellierung-zusammenfassung","text":""},{"seite":"lp.html","titel":"Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise","anker":null,"text":""},{"seite":"lp.html","titel":"5.1 In 5 Minuten gelöst","anker":"sec:lp-schnellstart","text":"Bisher hat ein Solver Ihnen gesagt, was Sie tun sollen. Jetzt sagt er Ihnen zusätzlich, was eine zusätzliche Stunde wert wäre — und das ist die Zahl, für die Sie im Betrieb gehört "},{"seite":"lp.html","titel":"5.2 Lernziele","anker":"sec:lp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"lp.html","titel":"5.3 Die Standardform und Schlupfvariablen","anker":"sec:lp-die-standardform-und-schlupfvariablen","text":"Lineare Programme zeichnen sich dadurch aus, dass Zielfunktion und alle Nebenbedingungen strikt linear sind — keine Produkte von Variablen, keine Quadrate, keine Logarithmen."},{"seite":"lp.html","titel":"5.4 Der Simplex-Algorithmus Schritt für Schritt","anker":"sec:lp-der-simplex-algorithmus-schritt-fuer-schritt","text":"Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus Kapitel 2: Das Optimum liegt in einer Ecke. Statt alle Ecken aufzuzählen, wandert er von E"},{"seite":"lp.html","titel":"Der Ablauf","anker":"der-ablauf","text":""},{"seite":"lp.html","titel":"5.5 Das Simplex-Tableau in Python","anker":"sec:lp-das-simplex-tableau-in-python","text":""},{"seite":"lp.html","titel":"5.6 Dualität und Schattenpreise","anker":"sec:lp-dualitaet-und-schattenpreise","text":"Zu jedem linearen Optimierungsproblem (dem primalen Problem) existiert ein spiegelbildliches Zwillingsproblem: das duale Problem."},{"seite":"lp.html","titel":"Der starke Dualitätssatz","anker":"der-starke-dualitätssatz","text":""},{"seite":"lp.html","titel":"Der Schattenpreis als Ableitung","anker":"der-schattenpreis-als-ableitung","text":"Der Dualwert y_i^* ist die Grenzproduktivität der Ressource i:"},{"seite":"lp.html","titel":"5.7 Die Vorzeichenfalle bei Schattenpreisen","anker":"sec:lp-die-vorzeichenfalle-bei-schattenpreisen","text":"Diese Falle sollten Sie sich merken, weil sie in jedem Projekt wieder auftaucht."},{"seite":"lp.html","titel":"5.8 Praxisfall: Sensitivitätsanalyse mit korrekten Schattenpreisen","anker":"sec:lp-praxisfall-sensitivitaetsanalyse-mit-korrekten-schattenpreisen","text":"Szenario. Ein Fertigungsbetrieb stellt drei Produkte her und will wissen:"},{"seite":"lp.html","titel":"5.9 Wann Schattenpreise lügen: Entartung und Toleranzen","anker":"sec:lp-entartung","text":"Der vorige Abschnitt hat gezeigt, wie wertvoll Schattenpreise sind. Dieser zeigt die zwei Fälle, in denen sie eine Entscheidung nicht tragen — und wie man beide erkennt, bevor jema"},{"seite":"lp.html","titel":"Fall 1: Entartung","anker":"fall-1-entartung","text":"Erinnern Sie sich an Abschnitt 2.4: In zwei Dimensionen legen zwei sich schneidende Geraden eine Ecke fest, in drei Dimensionen drei Ebenen, allgemein n Restriktionen bei n Variabl"},{"seite":"lp.html","titel":"Fall 2: Toleranzen","anker":"fall-2-toleranzen","text":"Der zweite Fall ist unscheinbarer und deshalb häufiger. Wie stellt man fest, ob eine Ressource ausgelastet ist? Naheliegend wäre if schlupf == 0. Genau das ist falsch."},{"seite":"lp.html","titel":"Die ehrliche Auskunft: Schattenpreis-Spannen","anker":"die-ehrliche-auskunft-schattenpreis-spannen","text":"Was tut man also, wenn das Modell entartet ist? Man meldet keinen Einzelwert, sondern die Spanne. Die Menge aller optimalen Dualvektoren ist selbst ein Polyeder:"},{"seite":"lp.html","titel":"5.10 Übungsaufgaben","anker":"sec:lp-uebungsaufgaben","text":""},{"seite":"lp.html","titel":"5.11 Finde den Denkfehler","anker":"sec:lp-denkfehler","text":""},{"seite":"lp.html","titel":"5.12 Micro-Quiz","anker":"sec:lp-quiz","text":""},{"seite":"lp.html","titel":"5.13 Selbsttest","anker":"sec:lp-selbsttest","text":""},{"seite":"lp.html","titel":"5.14 Zusammenfassung","anker":"sec:lp-zusammenfassung","text":""},{"seite":"milp.html","titel":"Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound","anker":null,"text":""},{"seite":"milp.html","titel":"6.1 In 5 Minuten gelöst","anker":"sec:milp-schnellstart","text":""},{"seite":"milp.html","titel":"6.2 Lernziele","anker":"sec:milp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"milp.html","titel":"6.3 Warum Runden fundamental scheitert","anker":"sec:milp-warum-runden-fundamental-scheitert","text":"In der realen Welt sind viele Entscheidungen nicht teilbar. Man kann nicht 0,47 Flugzeuge kaufen, keine halbe Lagerhalle bauen, keinen Mitarbeiter zu 38 % einstellen. Noch wichtige"},{"seite":"milp.html","titel":"6.4 Branch-and-Bound","anker":"sec:milp-branch-and-bound","text":"MILP-Probleme sind NP-schwer. Der Standardansatz moderner Solver (HiGHS, SCIP, Gurobi) heißt Branch-and-Cut — Branch-and-Bound plus Schnittebenen."},{"seite":"milp.html","titel":"Die drei Phasen","anker":"die-drei-phasen","text":""},{"seite":"milp.html","titel":"6.5 Modellierungstricks: Big-M und logische Bedingungen","anker":"sec:milp-modellierungstricks-big-m-und-logische-bedingungen","text":"Hier kommt das Handwerkszeug, das MILP so mächtig macht: Man übersetzt Geschäftsregeln in lineare Ungleichungen — mithilfe binärer Hilfsvariablen y \\in \\{0,1\\} und einer hinreichen"},{"seite":"milp.html","titel":"Muster 1 — Fixkosten / Aktivierungsschalter","anker":"muster-1-fixkosten-aktivierungsschalter","text":"Regel: Wird Maschine j genutzt (x_j > 0), fallen Rüstkosten F_j an."},{"seite":"milp.html","titel":"Muster 2 — Entweder-Oder (disjunctive constraints)","anker":"muster-2-entweder-oder-disjunctive-constraints","text":"Regel: Es muss entweder f(\\mathbf{x}) \\le b_1 oder g(\\mathbf{x}) \\le b_2 gelten."},{"seite":"milp.html","titel":"Muster 3 — Wenn-Dann (Implikation)","anker":"muster-3-wenn-dann-implikation","text":"Regel: Wenn y_1 = 1, dann muss auch y_2 = 1 sein."},{"seite":"milp.html","titel":"Muster 4 — Kardinalität („höchstens K aus N“)","anker":"muster-4-kardinalität-höchstens-k-aus-n","text":"\\sum_{j=1}^N y_j \\le K"},{"seite":"milp.html","titel":"Muster 5 — Semikontinuierlich („entweder 0 oder mindestens L“)","anker":"muster-5-semikontinuierlich-entweder-0-oder-mindestens-l","text":"Regel: Eine Position ist entweder gar nicht besetzt oder mit mindestens L Euro."},{"seite":"milp.html","titel":"6.6 Beispiel: Das Rucksackproblem","anker":"sec:milp-beispiel-das-rucksackproblem","text":"Bevor wir zum Portfolio kommen, das klassische Einstiegsproblem — kurz, verständlich und überall wiederzuerkennen."},{"seite":"milp.html","titel":"6.7 Praxisfall: Portfolio mit Ordergebühren und Kardinalitätsgrenze","anker":"sec:milp-praxisfall-portfolio-mit-ordergebuehren-und-kardinalitaetsgrenze","text":"Szenario. Ein Investor verteilt 100\\,000\\,\\text{€} auf sechs Anlageklassen."},{"seite":"milp.html","titel":"6.8 Wenn der Solver nicht fertig wird: Gap, Zeitlimit und Warm-Start","anker":"sec:milp-gap","text":"Bei einem LP gibt es zwei Ausgänge: eine optimale Lösung oder eine klare Absage. Bei einem MILP gibt es einen dritten, und im Betrieb ist er der häufigste:"},{"seite":"milp.html","titel":"Die zwei Zahlen, die zählen","anker":"die-zwei-zahlen-die-zählen","text":"Branch-and-Bound führt zu jedem Zeitpunkt zwei Werte mit:"},{"seite":"milp.html","titel":"Alle Statusfälle behandeln","anker":"alle-statusfälle-behandeln","text":"Der zweite Teil der Professionalität ist unspektakulär, aber entscheidend: jeden Rückgabewert auswerten, nicht nur OPTIMAL."},{"seite":"milp.html","titel":"Warm-Starts: was sie können und was nicht","anker":"warm-starts-was-sie-können-und-was-nicht","text":"Ein Warm-Start gibt dem Solver eine bekannte Lösung als Startpunkt mit. Die Idee ist verlockend: Wer schon eine brauchbare Lösung hat, muss nicht bei null anfangen."},{"seite":"milp.html","titel":"6.9 Übungsaufgaben","anker":"sec:milp-uebungsaufgaben","text":""},{"seite":"milp.html","titel":"6.10 Finde den Denkfehler","anker":"sec:milp-denkfehler","text":"Die Warnung „M so klein wie möglich“ wird meist mit der Laufzeit begründet: schwache Relaxation, mehr Knoten. Das stimmt — und ist die harmlosere Hälfte der Wahrheit."},{"seite":"milp.html","titel":"6.11 Micro-Quiz","anker":"sec:milp-quiz","text":""},{"seite":"milp.html","titel":"6.12 Selbsttest","anker":"sec:milp-selbsttest","text":""},{"seite":"milp.html","titel":"6.13 Zusammenfassung","anker":"sec:milp-zusammenfassung","text":""},{"seite":"cpsat.html","titel":"Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung","anker":null,"text":""},{"seite":"cpsat.html","titel":"7.1 In 5 Minuten gelöst","anker":"sec:cpsat-schnellstart","text":""},{"seite":"cpsat.html","titel":"7.2 Lernziele","anker":"sec:cpsat-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"cpsat.html","titel":"7.3 Ein anderes Denkmodell","anker":"sec:cpsat-ein-anderes-denkmodell","text":"Während LP und MILP auf algebraischen Gleichungen und Simplex-Matrizen beruhen, kommt Constraint Programming (CP) aus der künstlichen Intelligenz:"},{"seite":"cpsat.html","titel":"Was Propagation leistet","anker":"was-propagation-leistet","text":""},{"seite":"cpsat.html","titel":"7.4 Globale Constraints — die Bausteine von CP-SAT","anker":"sec:cpsat-globale-constraints-die-bausteine-von-cp","text":"CP-SAT stellt mächtige Makros bereit, die in MILP hunderte Big-M-Ungleichungen erfordern würden. Jedes davon hat einen spezialisierten, hocheffizienten Propagator."},{"seite":"cpsat.html","titel":"7.5 Praxisfall: Dynamisches Vertretungssystem","anker":"sec:cpsat-praxisfall-dynamisches-vertretungssystem","text":"Problemstellung. Eine Schule muss nach spontanen Ausfällen vier Unterrichtsstunden nachbesetzen."},{"seite":"cpsat.html","titel":"7.6 Intervallvariablen: Job-Shop-Scheduling","anker":"sec:cpsat-intervallvariablen-job-shop-scheduling","text":"Die Königsdisziplin von CP-SAT sind Zeitplanungsprobleme. Intervallvariablen sind dabei das zentrale Werkzeug."},{"seite":"cpsat.html","titel":"7.7 Die fünf Antworten von CP-SAT","anker":"sec:cpsat-statusfaelle","text":"Abschnitt 6.8 hat gezeigt, warum OPTIMAL bei ganzzahligen Problemen nicht der Normalfall ist. Bei CP-SAT kommt eine Besonderheit hinzu: Es gibt fünf verschiedene Antworten, und dre"},{"seite":"cpsat.html","titel":"7.8 Übungsaufgaben","anker":"sec:cpsat-uebungsaufgaben","text":""},{"seite":"cpsat.html","titel":"7.9 Finde den Denkfehler","anker":"sec:cpsat-denkfehler","text":""},{"seite":"cpsat.html","titel":"7.10 Micro-Quiz","anker":"sec:cpsat-quiz","text":""},{"seite":"cpsat.html","titel":"7.11 Selbsttest","anker":"sec:cpsat-selbsttest","text":""},{"seite":"cpsat.html","titel":"7.12 Zusammenfassung","anker":"sec:cpsat-zusammenfassung","text":""},{"seite":"graphen.html","titel":"Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP","anker":null,"text":""},{"seite":"graphen.html","titel":"8.1 In 5 Minuten gelöst","anker":"sec:graphen-schnellstart","text":""},{"seite":"graphen.html","titel":"8.2 Lernziele","anker":"sec:graphen-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"graphen.html","titel":"8.3 Graphen als Modellsprache","anker":"sec:graphen-graphen-als-modellsprache","text":"Viele reale Probleme in Logistik, Kommunikation und Finanzströmen sind keine flachen Ungleichungssysteme, sondern Graphen G = (V, E):"},{"seite":"graphen.html","titel":"Das Minimum-Cost-Flow-Problem (MCNFP)","anker":"das-minimum-cost-flow-problem-mcnfp","text":"Minimum-Cost Network Flow Problem — deutsch: kostenminimales Flussproblem. Es ist das mathematische Fundament für Transportketten, Liquiditätsrouting und Datenverteilung."},{"seite":"graphen.html","titel":"8.4 Bipartites Matching: das Zuordnungsproblem","anker":"sec:graphen-bipartites-matching-das-zuordnungsproblem","text":"Wenn N Aufgaben auf N Ressourcen eins zu eins verteilt werden — Orders auf Broker, Schichten auf Mitarbeitende, Aufträge auf Maschinen — spricht man von bipartitem Matching."},{"seite":"graphen.html","titel":"Der Satz von Birkhoff und von Neumann","anker":"der-satz-von-birkhoff-und-von-neumann","text":""},{"seite":"graphen.html","titel":"8.5 Das Vehicle Routing Problem mit Zeitfenstern","anker":"sec:graphen-das-vehicle-routing-problem-mit-zeitfenstern","text":"Das Traveling Salesperson Problem (TSP), deutsch Problem des Handlungsreisenden, fragt nach der kürzesten Rundreise durch N Städte. Das Capacitated Vehicle Routing Problem with Tim"},{"seite":"graphen.html","titel":"Kurzzyklen verhindern","anker":"kurzzyklen-verhindern","text":"Ein naives Modell erlaubt Subtouren: isolierte Kreise, die das Depot nie anfahren. Die klassische Gegenmaßnahme ist die MTZ-Formulierung nach Miller, Tucker und Zemlin. Man führt R"},{"seite":"graphen.html","titel":"Praxisbeispiel: Flotten-Routing","anker":"praxisbeispiel-flotten-routing","text":""},{"seite":"graphen.html","titel":"8.6 Übungsaufgaben","anker":"sec:graphen-uebungsaufgaben","text":""},{"seite":"graphen.html","titel":"8.7 Finde den Denkfehler","anker":"sec:graphen-denkfehler","text":""},{"seite":"graphen.html","titel":"8.8 Micro-Quiz","anker":"sec:graphen-quiz","text":""},{"seite":"graphen.html","titel":"8.9 Selbsttest","anker":"sec:graphen-selbsttest","text":""},{"seite":"graphen.html","titel":"8.10 Zusammenfassung","anker":"sec:graphen-zusammenfassung","text":""},{"seite":"metaheuristiken.html","titel":"Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt","anker":null,"text":""},{"seite":"metaheuristiken.html","titel":"9.1 In 5 Minuten gelöst","anker":"sec:metaheuristiken-schnellstart","text":""},{"seite":"metaheuristiken.html","titel":"9.2 Lernziele","anker":"sec:metaheuristiken-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"metaheuristiken.html","titel":"9.3 Die Aufgabe: Rüstzeiten an der Lackieranlage","anker":"sec:metaheuristiken-aufgabe","text":"Der Schnellstart war ein Spielzeug. Die Aufgabe dieses Kapitels ist dieselbe, nur in Betriebsgröße: n Aufträge in eine Reihenfolge bringen, so dass die Summe der Rüstzeiten minimal"},{"seite":"metaheuristiken.html","titel":"Die Instanz","anker":"die-instanz","text":"Alle Programme dieses Kapitels benutzen dieselbe Aufgabe. Die Rüstzeit setzt sich aus zwei Teilen zusammen: einem unregelmäßigen Reinigungsaufwand zwischen den Produktfamilien und "},{"seite":"metaheuristiken.html","titel":"9.4 Lokale Suche: der Zug und seine Kosten","anker":"sec:metaheuristiken-lokale-suche","text":"Eine lokale Suche braucht drei Dinge: eine Startlösung, einen Zug und eine Regel, welche Züge übernommen werden."},{"seite":"metaheuristiken.html","titel":"9.5 Simulated Annealing","anker":"sec:metaheuristiken-annealing","text":"Reines Bergsteigen — nur Verbesserungen annehmen — bleibt im ersten lokalen Optimum stehen. Simulated Annealing nimmt Verschlechterungen mit einer Wahrscheinlichkeit an, die von de"},{"seite":"metaheuristiken.html","titel":"Was die Messung zeigt — und was sie nicht zeigt","anker":"was-die-messung-zeigt-und-was-sie-nicht-zeigt","text":"Erstens: Der Beitrag des Annealings ist bescheiden. Bergsteigen kommt auf 917 Minuten, Annealing auf 907 — das simple Verfahren holt 93 % des Ertrags. Das ist kein Argument gegen A"},{"seite":"metaheuristiken.html","titel":"9.6 Ab wann lohnt es sich?","anker":"sec:metaheuristiken-umschlagpunkt","text":"„Kleine Probleme exakt, große mit Metaheuristiken“ ist richtig und nutzlos, solange niemand sagt, wo groß anfängt. Diese Zahl lässt sich messen."},{"seite":"metaheuristiken.html","titel":"Die Spalte, die man nicht weglassen darf","anker":"die-spalte-die-man-nicht-weglassen-darf","text":"Bei 500 Aufträgen haben wir die CP-SAT-Lösung verworfen. Seine Schranke ist trotzdem das Wertvollste in der ganzen Tabelle."},{"seite":"metaheuristiken.html","titel":"9.7 Large Neighborhood Search","anker":"sec:metaheuristiken-lns","text":"Die Tabelle oben endet mit einem Widerspruch. Bei 500 Aufträgen scheitert der exakte Solver an der Größe; die Metaheuristik scheitert an der Kleinheit ihrer Züge — sie verschiebt j"},{"seite":"metaheuristiken.html","titel":"Das Fenster ist die eine Stellschraube","anker":"das-fenster-ist-die-eine-stellschraube","text":""},{"seite":"metaheuristiken.html","titel":"Das Gesamtbild","anker":"das-gesamtbild","text":""},{"seite":"metaheuristiken.html","titel":"9.8 Was dieses Kapitel nicht behandelt","anker":"sec:metaheuristiken-abgrenzung","text":"Damit die Landkarte vollständig ist — drei Verfahren, die in dieselbe Familie gehören und hier bewusst nur benannt werden:"},{"seite":"metaheuristiken.html","titel":"9.9 Übungsaufgaben","anker":"sec:metaheuristiken-uebungsaufgaben","text":""},{"seite":"metaheuristiken.html","titel":"9.10 Finde den Denkfehler","anker":"sec:metaheuristiken-denkfehler","text":""},{"seite":"metaheuristiken.html","titel":"9.11 Micro-Quiz","anker":"sec:metaheuristiken-quiz","text":""},{"seite":"metaheuristiken.html","titel":"9.12 Selbsttest","anker":"sec:metaheuristiken-selbsttest","text":""},{"seite":"metaheuristiken.html","titel":"9.13 Zusammenfassung","anker":"sec:metaheuristiken-zusammenfassung","text":""},{"seite":"dekomposition.html","titel":"Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten","anker":null,"text":""},{"seite":"dekomposition.html","titel":"10.1 In 5 Minuten gelöst","anker":"sec:dekomposition-schnellstart","text":""},{"seite":"dekomposition.html","titel":"10.2 Lernziele","anker":"sec:dekomposition-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"dekomposition.html","titel":"10.3 Warum das naheliegende Modell scheitert","anker":"sec:dekomposition-symmetrie","text":"Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable z_{jr} für „Stück j liegt auf Rolle r“, dazu y_r für „Rolle r wird benutzt”."},{"seite":"dekomposition.html","titel":"10.4 Rechnen mit Mustern, ohne sie aufzuschreiben","anker":"sec:dekomposition-schleife","text":"Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:"},{"seite":"dekomposition.html","titel":"10.5 Das Programm","anker":"sec:dekomposition-programm","text":""},{"seite":"dekomposition.html","titel":"10.6 Was die Zahlen zeigen","anker":"sec:dekomposition-befund","text":""},{"seite":"dekomposition.html","titel":"Die Musterzahl explodiert, der Bedarf nicht","anker":"die-musterzahl-explodiert-der-bedarf-nicht","text":""},{"seite":"dekomposition.html","titel":"Der unbequeme Teil: Es lohnt sich nicht immer","anker":"der-unbequeme-teil-es-lohnt-sich-nicht-immer","text":""},{"seite":"dekomposition.html","titel":"10.7 Übungsaufgaben","anker":"sec:dekomposition-uebungsaufgaben","text":""},{"seite":"dekomposition.html","titel":"10.8 Finde den Denkfehler","anker":"sec:dekomposition-denkfehler","text":""},{"seite":"dekomposition.html","titel":"10.9 Micro-Quiz","anker":"sec:dekomposition-quiz","text":""},{"seite":"dekomposition.html","titel":"10.10 Selbsttest","anker":"sec:dekomposition-selbsttest","text":""},{"seite":"dekomposition.html","titel":"10.11 Zusammenfassung","anker":"sec:dekomposition-zusammenfassung","text":""},{"seite":"qp-nlp.html","titel":"Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität","anker":null,"text":""},{"seite":"qp-nlp.html","titel":"11.1 In 5 Minuten gelöst","anker":"sec:qp-nlp-schnellstart","text":""},{"seite":"qp-nlp.html","titel":"11.2 Lernziele","anker":"sec:qp-nlp-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"qp-nlp.html","titel":"Auffrischung: Gradient in einer Minute","anker":"auffrischung-gradient-in-einer-minute","text":"Der Gradient \\nabla f(\\mathbf{x}) ist der Vektor aller partiellen Ableitungen:"},{"seite":"qp-nlp.html","titel":"11.3 Das quadratische Programm (QP)","anker":"sec:qp-nlp-das-quadratische-programm-qp","text":"Ein quadratisches Programm hat eine quadratische Zielfunktion und lineare Nebenbedingungen:"},{"seite":"qp-nlp.html","titel":"Konvexität: die präzise Aussage","anker":"konvexität-die-präzise-aussage","text":"Eine verbreitete, aber zu stark formulierte Aussage lautet: „Wenn \\mathbf{P} positiv semi-definit ist, ist das QP streng konvex; es existiert ein eindeutiges globales Minimum.“ Kor"},{"seite":"qp-nlp.html","titel":"11.4 Die Karush-Kuhn-Tucker-Bedingungen","anker":"sec:qp-nlp-die-karush-kuhn-tucker-bedingungen","text":"Die KKT-Bedingungen verallgemeinern den Lagrange-Ansatz auf Probleme mit Ungleichungen. Sie sind notwendig für ein Optimum — und bei konvexen Problemen auch hinreichend."},{"seite":"qp-nlp.html","titel":"Die vier KKT-Bedingungen","anker":"die-vier-kkt-bedingungen","text":"Am Optimum (\\mathbf{x}^*, \\boldsymbol{\\lambda}^*, \\boldsymbol{\\nu}^*) gilt:"},{"seite":"qp-nlp.html","titel":"11.5 Nichtlineare Optimierung mit scipy.optimize.minimize","anker":"sec:qp-nlp-nichtlineare-optimierung-mit-scipy-optimize-minimize","text":"Wenn Zielfunktion oder Nebenbedingungen weder linear noch quadratisch sind — Logarithmen, Wurzeln, Exponentialfunktionen — nutzt man gradientenbasierte Verfahren wie SLSQP (Sequent"},{"seite":"qp-nlp.html","titel":"Praxisfall: Entropie-maximierte Kapitalallokation","anker":"praxisfall-entropie-maximierte-kapitalallokation","text":"Wir optimieren ein Portfolio nicht nur nach Varianz, sondern maximieren zusätzlich die Shannon-Entropie der Gewichte, um Klumpenrisiken glatt zu bestrafen:"},{"seite":"qp-nlp.html","titel":"11.6 Jenseits der Konvexität: lokale Optima, Multistart und MINLP","anker":"sec:qp-nlp-nichtkonvex","text":"Alles bisher in diesem Kapitel stand unter einer Bedingung: Konvexität. Sie ist der Grund, warum CVXPY eine Optimalitätsgarantie geben kann und warum die KKT-Bedingungen nicht nur "},{"seite":"qp-nlp.html","titel":"Woher Nicht-Konvexität im Alltag kommt","anker":"woher-nicht-konvexität-im-alltag-kommt","text":"Nicht aus exotischer Mathematik, sondern aus ganz gewöhnlichen betriebswirtschaftlichen Effekten:"},{"seite":"qp-nlp.html","titel":"Was ein lokales Optimum praktisch bedeutet","anker":"was-ein-lokales-optimum-praktisch-bedeutet","text":"scipy.optimize.minimize verweigert nichts. Es rechnet, meldet success: True und liefert eine Zahl. Diese Meldung heißt aber nicht „das ist das Optimum“, sondern nur:"},{"seite":"qp-nlp.html","titel":"Die Zahlen im Klartext","anker":"die-zahlen-im-klartext","text":""},{"seite":"qp-nlp.html","titel":"Ausblick: MINLP und globale Solver","anker":"ausblick-minlp-und-globale-solver","text":"Wird zusätzlich noch ganzzahlig entschieden — „welche Anlage läuft überhaupt“ und „wie stark“ —, entsteht ein MINLP (Mixed-Integer Nonlinear Program), die schwierigste der in diese"},{"seite":"qp-nlp.html","titel":"11.7 Übungsaufgaben","anker":"sec:qp-nlp-uebungsaufgaben","text":""},{"seite":"qp-nlp.html","titel":"11.8 Finde den Denkfehler","anker":"sec:qp-nlp-denkfehler","text":""},{"seite":"qp-nlp.html","titel":"11.9 Micro-Quiz","anker":"sec:qp-nlp-quiz","text":""},{"seite":"qp-nlp.html","titel":"11.10 Selbsttest","anker":"sec:qp-nlp-selbsttest","text":""},{"seite":"qp-nlp.html","titel":"11.11 Zusammenfassung","anker":"sec:qp-nlp-zusammenfassung","text":""},{"seite":"unsicherheit.html","titel":"Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit","anker":null,"text":""},{"seite":"unsicherheit.html","titel":"12.1 In 5 Minuten gelöst","anker":"sec:unsicherheit-schnellstart","text":""},{"seite":"unsicherheit.html","titel":"12.2 Lernziele","anker":"sec:unsicherheit-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"unsicherheit.html","titel":"12.3 Der Fluch des Durchschnitts","anker":"sec:unsicherheit-der-fluch-des-durchschnitts","text":"Der naheliegende Umgang mit Unsicherheit lautet: „Wir setzen den Erwartungswert ein und rechnen deterministisch.“ Das ist fast immer falsch — und zwar nicht ungenau, sondern system"},{"seite":"unsicherheit.html","titel":"Die vier Ansätze im Überblick","anker":"die-vier-ansätze-im-überblick","text":""},{"seite":"unsicherheit.html","titel":"12.4 Monte-Carlo-Simulation","anker":"sec:unsicherheit-monte-carlo-simulation","text":"Monte-Carlo heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan aus, und betrachte die Verteilung der Ergebnisse statt eines einzigen Werts."},{"seite":"unsicherheit.html","titel":"12.5 Zweistufige stochastische Programmierung","anker":"sec:unsicherheit-zweistufige-stochastische-programmierung","text":"Das zweistufige Modell mit Recourse bildet ab, dass Entscheidungen zeitlich gestaffelt sind:"},{"seite":"unsicherheit.html","titel":"12.6 Robuste Optimierung: gegen den Worst Case absichern","anker":"sec:unsicherheit-robuste-optimierung-gegen-den-worst-case","text":"Wenn Wahrscheinlichkeiten unbekannt oder instabil sind — Marktcrashs, Lieferkettenabrisse, Pandemien —, hilft die robuste Optimierung. Sie fragt nicht nach dem Mittel, sondern nach"},{"seite":"unsicherheit.html","titel":"12.7 Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“","anker":"sec:unsicherheit-chance-constraints","text":"Die drei bisherigen Ansätze beantworten Fragen, die im Sitzungssaal selten so gestellt werden. „Was ist im Mittel am besten?“ hört ein Vorstand als Ausflucht; „was hält auch im sch"},{"seite":"unsicherheit.html","titel":"Weg 1: Normalverteilung — die Bedingung wird ein Kegel","anker":"weg-1-normalverteilung-die-bedingung-wird-ein-kegel","text":"Ist \\mathbf{a} \\sim \\mathcal{N}(\\hat{\\mathbf{a}}, \\boldsymbol{\\Sigma}), dann ist \\mathbf{a}^\\top\\mathbf{x} für festes \\mathbf{x} selbst normalverteilt, mit Erwartungswert \\hat{\\mat"},{"seite":"unsicherheit.html","titel":"Weg 2: Szenarien — Big-M ohne Verteilungsannahme","anker":"weg-2-szenarien-big-m-ohne-verteilungsannahme","text":"Liegen statt einer Verteilung nur S beobachtete Szenarien vor, bekommt jedes eine Binärvariable z_s, die sagt, ob es verletzt werden darf:"},{"seite":"unsicherheit.html","titel":"Der Fall: ein Kraftwerkspark mit 500 MW Zusage","anker":"der-fall-ein-kraftwerkspark-mit-500-mw-zusage","text":""},{"seite":"unsicherheit.html","titel":"12.8 Übungsaufgaben","anker":"sec:unsicherheit-uebungsaufgaben","text":""},{"seite":"unsicherheit.html","titel":"12.9 Finde den Denkfehler","anker":"sec:unsicherheit-denkfehler","text":""},{"seite":"unsicherheit.html","titel":"12.10 Micro-Quiz","anker":"sec:unsicherheit-quiz","text":""},{"seite":"unsicherheit.html","titel":"12.11 Selbsttest","anker":"sec:unsicherheit-selbsttest","text":""},{"seite":"unsicherheit.html","titel":"12.12 Zusammenfassung","anker":"sec:unsicherheit-zusammenfassung","text":""},{"seite":"dynamische-programmierung.html","titel":"Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution","anker":null,"text":""},{"seite":"dynamische-programmierung.html","titel":"13.1 In 5 Minuten gelöst","anker":"sec:dynamische-programmierung-schnellstart","text":""},{"seite":"dynamische-programmierung.html","titel":"13.2 Lernziele","anker":"sec:dynamische-programmierung-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"dynamische-programmierung.html","titel":"13.3 Das Bellmansche Optimalitätsprinzip","anker":"sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip","text":"Richard Bellman formulierte 1957 das Grundprinzip der Dynamischen Programmierung (DP):"},{"seite":"dynamische-programmierung.html","titel":"Die vier Bausteine eines DP-Modells","anker":"die-vier-bausteine-eines-dp-modells","text":""},{"seite":"dynamische-programmierung.html","titel":"Die Bellman-Gleichung","anker":"die-bellman-gleichung","text":"V_t(S_t) = \\min_{a_t\\in\\mathcal{A}(S_t)}\\Big\\{\\,C(S_t,a_t) \\;+\\; \\gamma\\cdot\\mathbb{E}\\big[V_{t+1}(S_{t+1})\\mid S_t,a_t\\big]\\Big\\}"},{"seite":"dynamische-programmierung.html","titel":"13.4 Das Almgren-Chriss-Problem: optimale Orderausführung","anker":"sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung","text":"Ein klassisches OR-Problem an Börsen: Ein institutioneller Händler muss X_0 = 100\\,000 Aktien innerhalb von T Handelsperioden verkaufen."},{"seite":"dynamische-programmierung.html","titel":"13.5 Der Fluch der Dimensionalität","anker":"sec:dynamische-programmierung-der-fluch-der-dimensionalitaet","text":"DP ist mächtig, aber es hat eine harte Grenze. Der Aufwand der Rückwärtsinduktion ist"},{"seite":"dynamische-programmierung.html","titel":"13.6 Übungsaufgaben","anker":"sec:dynamische-programmierung-uebungsaufgaben","text":""},{"seite":"dynamische-programmierung.html","titel":"13.7 Finde den Denkfehler","anker":"sec:dynamische-programmierung-denkfehler","text":"Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder Misserfolg: Was gehört in den Zustand? Ist er zu klein, rechnet das Verfahren völlig korrekt — nur eb"},{"seite":"dynamische-programmierung.html","titel":"13.8 Micro-Quiz","anker":"sec:dynamische-programmierung-quiz","text":""},{"seite":"dynamische-programmierung.html","titel":"13.9 Selbsttest","anker":"sec:dynamische-programmierung-selbsttest","text":""},{"seite":"dynamische-programmierung.html","titel":"13.10 Zusammenfassung","anker":"sec:dynamische-programmierung-zusammenfassung","text":""},{"seite":"mehrziel.html","titel":"Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte","anker":null,"text":""},{"seite":"mehrziel.html","titel":"14.1 In 5 Minuten gelöst","anker":"sec:mehrziel-schnellstart","text":""},{"seite":"mehrziel.html","titel":"14.2 Lernziele","anker":"sec:mehrziel-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"mehrziel.html","titel":"14.3 Wann ein Kompromiss gut ist","anker":"sec:mehrziel-dominanz","text":"Bei einem Ziel ist „besser” eindeutig. Bei zwei Zielen nicht mehr: Ein Plan kann billiger und schmutziger sein als ein anderer, und dann ist keiner von beiden besser."},{"seite":"mehrziel.html","titel":"14.4 Der Reflex: alles in ein Ziel rühren","anker":"sec:mehrziel-skalarisierung","text":"Der naheliegende Weg, zwei Ziele zu einem zu machen, ist die gewichtete Summe:"},{"seite":"mehrziel.html","titel":"14.5 Das Programm","anker":"sec:mehrziel-programm","text":"Zwölf Sendungen, drei Verkehrsträger, fünf freie Bahntrassen. Der Zielkonflikt entsteht nicht zwischen den Trägern — die Bahn ist immer billiger und sauberer als der LKW —, sondern"},{"seite":"mehrziel.html","titel":"14.6 Was die gewichtete Summe nicht sieht","anker":"sec:mehrziel-luecke","text":"Die Front hat zehn Punkte. Ein Raster aus 1 401 Gewichten findet sechs davon. Vier Kompromisse sind für kein Gewicht erreichbar — und das ist keine Frage der Rasterweite."},{"seite":"mehrziel.html","titel":"Das Gewicht ist keine Feineinstellung","anker":"das-gewicht-ist-keine-feineinstellung","text":"Die zweite Beobachtung aus derselben Tabelle: 1 401 Gewichte erzeugen sechs verschiedene Pläne. Weite Gewichtsbereiche liefern dasselbe Ergebnis, und dazwischen springt es."},{"seite":"mehrziel.html","titel":"14.7 Das ε-Constraint-Verfahren","anker":"sec:mehrziel-epsilon","text":"Der Ausweg ist unspektakulär: Man macht aus dem zweiten Ziel eine Nebenbedingung."},{"seite":"mehrziel.html","titel":"Wenn es eine klare Rangfolge gibt: lexikografisch","anker":"wenn-es-eine-klare-rangfolge-gibt-lexikografisch","text":"Manchmal ist ein Ziel wirklich vorrangig. Dann optimiert man lexikografisch: erst das erste Ziel, dann das zweite unter der Bedingung, dass das erste (fast) erhalten bleibt."},{"seite":"mehrziel.html","titel":"14.8 Wie man die Front vorlegt","anker":"sec:mehrziel-vorlegen","text":"Eine Front mit zehn Punkten ist kein Ergebnis, sondern eine Speisekarte. Damit sie entscheidbar wird, gehört eine Spalte dazu, die im Programm EUR je kg heißt:"},{"seite":"mehrziel.html","titel":"14.9 Übungsaufgaben","anker":"sec:mehrziel-uebungsaufgaben","text":""},{"seite":"mehrziel.html","titel":"14.10 Finde den Denkfehler","anker":"sec:mehrziel-denkfehler","text":""},{"seite":"mehrziel.html","titel":"14.11 Micro-Quiz","anker":"sec:mehrziel-quiz","text":""},{"seite":"mehrziel.html","titel":"14.12 Selbsttest","anker":"sec:mehrziel-selbsttest","text":""},{"seite":"mehrziel.html","titel":"14.13 Zusammenfassung","anker":"sec:mehrziel-zusammenfassung","text":""},{"seite":"prognose.html","titel":"Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung","anker":null,"text":""},{"seite":"prognose.html","titel":"15.1 In 5 Minuten gelöst","anker":"sec:prognose-schnellstart","text":""},{"seite":"prognose.html","titel":"15.2 Lernziele","anker":"sec:prognose-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"prognose.html","titel":"15.3 Die Naht zwischen zwei Abteilungen","anker":"sec:prognose-naht","text":"In fast jedem Unternehmen sieht der Ablauf so aus:"},{"seite":"prognose.html","titel":"15.4 Das Programm","anker":"sec:prognose-programm","text":"Dieselbe Bäckerei, aber mit Merkmalen: Wochentag, Temperatur, Aktionstage. Die Nachfrage muss jetzt modelliert werden."},{"seite":"prognose.html","titel":"15.5 Der Befund","anker":"sec:prognose-befund","text":""},{"seite":"prognose.html","titel":"Warum ein pauschaler Zuschlag zu kurz greift","anker":"warum-ein-pauschaler-zuschlag-zu-kurz-greift","text":"Die zweite Zeile der Tabelle ist die verbreitete Praxis: Punktprognose plus Sicherheitsbestand. Sie hilft (−7,3 %), bleibt aber hinter der Quantilregression zurück. Das Programm re"},{"seite":"prognose.html","titel":"15.6 Eine Messfalle, in die der Autor selbst getappt ist","anker":"sec:prognose-messfalle","text":"Der erste Entwurf dieses Programms bewertete auf 230 Testtagen — ein realistischer Zeitraum, gut sieben Monate. Dort hatte die Quantilregression den besseren MSE, und die ganze Aus"},{"seite":"prognose.html","titel":"15.7 Wie weit das Verfahren reicht","anker":"sec:prognose-ausblick","text":"Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert” — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: das Prognosemodell direkt auf die "},{"seite":"prognose.html","titel":"15.8 Übungsaufgaben","anker":"sec:prognose-uebungsaufgaben","text":""},{"seite":"prognose.html","titel":"15.9 Finde den Denkfehler","anker":"sec:prognose-denkfehler","text":""},{"seite":"prognose.html","titel":"15.10 Micro-Quiz","anker":"sec:prognose-quiz","text":""},{"seite":"prognose.html","titel":"15.11 Selbsttest","anker":"sec:prognose-selbsttest","text":""},{"seite":"prognose.html","titel":"15.12 Zusammenfassung","anker":"sec:prognose-zusammenfassung","text":""},{"seite":"bruecke.html","titel":"Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten","anker":null,"text":""},{"seite":"bruecke.html","titel":"16.1 In 5 Minuten gelöst","anker":"sec:bruecke-schnellstart","text":""},{"seite":"bruecke.html","titel":"16.2 Lernziele","anker":"sec:bruecke-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"bruecke.html","titel":"16.3 Das Programm","anker":"sec:bruecke-programm","text":""},{"seite":"bruecke.html","titel":"16.4 Die zwei Brücken im Einzelnen","anker":"sec:bruecke-einzelnen","text":""},{"seite":"bruecke.html","titel":"Erste Brücke: Allokation","anker":"erste-brücke-allokation","text":""},{"seite":"bruecke.html","titel":"Zweite Brücke: Absicherung gegen den schlechtesten Fall","anker":"zweite-brücke-absicherung-gegen-den-schlechtesten-fall","text":"Hier ist die Übereinstimmung noch enger, weil sie nicht nur die Struktur betrifft, sondern die Formel. Die Rockafellar-Uryasev-Formulierung aus Kapitel 20"},{"seite":"bruecke.html","titel":"Wo die Lösungen sich unterscheiden","anker":"wo-die-lösungen-sich-unterscheiden","text":"Der Vergleich fördert nebenbei einen Unterschied zutage, den man nicht erwartet: Die Streuungsgrenze von 40 % bindet nur im Einkauf, nicht im Depot."},{"seite":"bruecke.html","titel":"16.5 Wo die Brücke endet","anker":"sec:bruecke-grenzen","text":"Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Dinge wandern nicht mit:"},{"seite":"bruecke.html","titel":"16.6 Übungsaufgaben","anker":"sec:bruecke-uebungsaufgaben","text":""},{"seite":"bruecke.html","titel":"16.7 Finde den Denkfehler","anker":"sec:bruecke-denkfehler","text":""},{"seite":"bruecke.html","titel":"16.8 Micro-Quiz","anker":"sec:bruecke-quiz","text":""},{"seite":"bruecke.html","titel":"16.9 Selbsttest","anker":"sec:bruecke-selbsttest","text":""},{"seite":"bruecke.html","titel":"16.10 Zusammenfassung","anker":"sec:bruecke-zusammenfassung","text":""},{"seite":"supplychain.html","titel":"Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit","anker":null,"text":""},{"seite":"supplychain.html","titel":"17.1 In 5 Minuten gelöst","anker":"sec:supplychain-schnellstart","text":""},{"seite":"supplychain.html","titel":"17.2 Lernziele","anker":"sec:supplychain-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"supplychain.html","titel":"17.3 Die Aufgabe","anker":"sec:supplychain-aufgabe","text":"Fünf Blöcke, 24 Stunden, eine Last zwischen 270 und 804 MW. Dazu Windeinspeisung, die niemand am Vorabend kennt."},{"seite":"supplychain.html","titel":"Die Zeitstruktur der Entscheidung","anker":"die-zeitstruktur-der-entscheidung","text":"Hier geht das Kapitel über Abschnitt 12.5 hinaus:"},{"seite":"supplychain.html","titel":"17.4 Das Programm","anker":"sec:supplychain-programm","text":""},{"seite":"supplychain.html","titel":"17.5 Der Befund","anker":"sec:supplychain-befund","text":""},{"seite":"supplychain.html","titel":"17.6 Was Versorgungssicherheit kostet","anker":"sec:supplychain-sicherheit","text":"Der dritte Plan legt eine CVaR-Schranke auf die nicht gedeckte Energie: Im Mittel der schlechtesten 10 % der Szenarien darf nichts fehlen. Dieselbe Rockafellar-Uryasev-Konstruktion"},{"seite":"supplychain.html","titel":"17.7 Übungsaufgaben","anker":"sec:supplychain-uebungsaufgaben","text":""},{"seite":"supplychain.html","titel":"17.8 Finde den Denkfehler","anker":"sec:supplychain-denkfehler","text":""},{"seite":"supplychain.html","titel":"17.9 Micro-Quiz","anker":"sec:supplychain-quiz","text":""},{"seite":"supplychain.html","titel":"17.10 Selbsttest","anker":"sec:supplychain-selbsttest","text":""},{"seite":"supplychain.html","titel":"17.11 Zusammenfassung","anker":"sec:supplychain-zusammenfassung","text":""},{"seite":"finanzdaten.html","titel":"Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage","anker":null,"text":""},{"seite":"finanzdaten.html","titel":"18.1 In 5 Minuten gelöst","anker":"sec:finanzdaten-schnellstart","text":""},{"seite":"finanzdaten.html","titel":"18.2 Lernziele","anker":"sec:finanzdaten-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"finanzdaten.html","titel":"18.3 Diskrete und logarithmische Renditen","anker":"sec:finanzdaten-diskrete-und-logarithmische-renditen","text":"Sei P_{i,t} der bereinigte Schlusskurs (adjusted close) von Titel i zum Zeitpunkt t. „Bereinigt“ heißt: um Dividenden und Aktiensplits korrigiert — sonst erscheint jede Dividendenz"},{"seite":"finanzdaten.html","titel":"Diskrete Rendite","anker":"diskrete-rendite","text":"R_{i,t} = \\frac{P_{i,t} - P_{i,t-1}}{P_{i,t-1}} = \\frac{P_{i,t}}{P_{i,t-1}} - 1"},{"seite":"finanzdaten.html","titel":"Logarithmische Rendite","anker":"logarithmische-rendite","text":"r_{i,t} = \\ln\\!\\left(\\frac{P_{i,t}}{P_{i,t-1}}\\right) = \\ln P_{i,t} - \\ln P_{i,t-1}"},{"seite":"finanzdaten.html","titel":"18.4 Das Schätzfehler-Problem","anker":"sec:finanzdaten-das-schaetzfehler-problem","text":"Sei \\mathbf{X}\\in\\mathbb{R}^{T\\times N} die Matrix zentrierter Renditen von N Titeln über T Handelstage. Die Stichproben-Kovarianzmatrix lautet:"},{"seite":"finanzdaten.html","titel":"18.5 Ledoit-Wolf-Shrinkage","anker":"sec:finanzdaten-ledoit-wolf-shrinkage","text":"Olivier Ledoit und Michael Wolf lösten das Dilemma durch lineare Schrumpfung:"},{"seite":"finanzdaten.html","titel":"Welches Ziel \\mathbf{F}?","anker":"welches-ziel-mathbff","text":"Ein häufiges Missverständnis: Es gibt nämlich mehrere Ledoit-Wolf-Varianten, und Text und Code müssen sich konsistent auf dieselbe beziehen:"},{"seite":"finanzdaten.html","titel":"18.6 Praxis: Datenpipeline mit korrekter Spaltenreihenfolge","anker":"sec:finanzdaten-praxis-datenpipeline-mit-korrekter-spaltenreihenfolge","text":""},{"seite":"finanzdaten.html","titel":"18.7 Übungsaufgaben","anker":"sec:finanzdaten-uebungsaufgaben","text":""},{"seite":"finanzdaten.html","titel":"18.8 Finde den Denkfehler","anker":"sec:finanzdaten-denkfehler","text":""},{"seite":"finanzdaten.html","titel":"18.9 Micro-Quiz","anker":"sec:finanzdaten-quiz","text":""},{"seite":"finanzdaten.html","titel":"18.10 Selbsttest","anker":"sec:finanzdaten-selbsttest","text":""},{"seite":"finanzdaten.html","titel":"18.11 Zusammenfassung","anker":"sec:finanzdaten-zusammenfassung","text":""},{"seite":"markowitz.html","titel":"Kapitel 19: Die moderne Portfoliotheorie nach Markowitz","anker":null,"text":""},{"seite":"markowitz.html","titel":"19.1 In 5 Minuten gelöst","anker":"sec:markowitz-schnellstart","text":""},{"seite":"markowitz.html","titel":"19.2 Lernziele","anker":"sec:markowitz-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"markowitz.html","titel":"19.3 Warum Diversifikation funktioniert","anker":"sec:markowitz-warum-diversifikation-funktioniert","text":"Harry Markowitz’ zentrale Einsicht von 1952: Das Risiko eines Portfolios ist nicht die Summe der Einzelrisiken. Es hängt davon ab, wie die Titel zusammenspielen."},{"seite":"markowitz.html","titel":"19.4 Das Mean-Variance-Modell","anker":"sec:markowitz-das-mean-variance-modell","text":"Sei \\mathbf{w} = (w_1,\\dots,w_n)^\\top der Vektor der Portfoliogewichte:"},{"seite":"markowitz.html","titel":"Die Effizienzgrenze und das Tangentialportfolio","anker":"die-effizienzgrenze-und-das-tangentialportfolio","text":"Die Effizienzgrenze (efficient frontier) ist die Kurve aller Portfolios, die zu jedem Risikoniveau die höchstmögliche Rendite liefern."},{"seite":"markowitz.html","titel":"Die Korn-Transformation","anker":"die-korn-transformation","text":"Die Sharpe Ratio ist ein Bruch — und damit nicht konvex. CVXPY würde sie ablehnen. Der Ausweg ist ein eleganter Trick, der die Homogenität des Problems ausnutzt:"},{"seite":"markowitz.html","titel":"19.5 Institutionelle Nebenbedingungen","anker":"sec:markowitz-institutionelle-nebenbedingungen","text":"In der Praxis gelten regulatorische und interne Regeln:"},{"seite":"markowitz.html","titel":"Was diese Regeln kosten","anker":"was-diese-regeln-kosten","text":"Jede Regel schneidet etwas vom Erreichbaren ab. Wie viel, lässt sich ausrechnen — man legt die Effizienzlinie ohne und mit Beschränkungen übereinander:"},{"seite":"markowitz.html","titel":"19.6 Vollimplementierung mit CVXPY","anker":"sec:markowitz-vollimplementierung-mit-cvxpy","text":""},{"seite":"markowitz.html","titel":"19.7 Übungsaufgaben","anker":"sec:markowitz-uebungsaufgaben","text":""},{"seite":"markowitz.html","titel":"19.8 Finde den Denkfehler","anker":"sec:markowitz-denkfehler","text":"Kapitel 18 hat gezeigt, was eine schlecht geschätzte Kovarianzmatrix anrichtet. Bei den erwarteten Renditen ist es schlimmer — und weil sie in der Zielfunktion stehen statt in der "},{"seite":"markowitz.html","titel":"19.9 Micro-Quiz","anker":"sec:markowitz-quiz","text":""},{"seite":"markowitz.html","titel":"19.10 Selbsttest","anker":"sec:markowitz-selbsttest","text":""},{"seite":"markowitz.html","titel":"19.11 Zusammenfassung","anker":"sec:markowitz-zusammenfassung","text":""},{"seite":"cvar.html","titel":"Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten","anker":null,"text":""},{"seite":"cvar.html","titel":"20.1 In 5 Minuten gelöst","anker":"sec:cvar-schnellstart","text":""},{"seite":"cvar.html","titel":"20.2 Lernziele","anker":"sec:cvar-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"cvar.html","titel":"20.3 Die zwei Schwächen des Markowitz-Modells","anker":"sec:cvar-die-zwei-schwaechen-des-markowitz-modells","text":"Erstens: die Normalverteilungs-Illusion. Die Varianz behandelt Aufwärts- und Abwärtsschwankungen gleich und unterstellt implizit symmetrische, dünn auslaufende Verteilungen. Reale "},{"seite":"cvar.html","titel":"20.4 Value at Risk und Conditional Value at Risk","anker":"sec:cvar-value-at-risk-und-conditional-value","text":""},{"seite":"cvar.html","titel":"Das Rockafellar-Uryasev-Theorem (2000)","anker":"das-rockafellar-uryasev-theorem-2000","text":"Rockafellar und Uryasev zeigten, dass sich der CVaR über S Szenarien exakt als lineares Programm formulieren lässt:"},{"seite":"cvar.html","titel":"20.5 Transaktionskosten über die L_1-Norm","anker":"sec:cvar-transaktionskosten-ueber-die-l-1-norm","text":"Sei \\mathbf{w}_{\\text{alt}} das bestehende Portfolio und \\mathbf{w} das neue Ziel. Der Umschlag (turnover) ist:"},{"seite":"cvar.html","titel":"20.6 Implementierung: CVaR-Portfolio mit Reibung","anker":"sec:cvar-implementierung-cvar-portfolio-mit-reibung","text":""},{"seite":"cvar.html","titel":"20.7 Übungsaufgaben","anker":"sec:cvar-uebungsaufgaben","text":""},{"seite":"cvar.html","titel":"20.8 Finde den Denkfehler","anker":"sec:cvar-denkfehler","text":""},{"seite":"cvar.html","titel":"20.9 Micro-Quiz","anker":"sec:cvar-quiz","text":""},{"seite":"cvar.html","titel":"20.10 Selbsttest","anker":"sec:cvar-selbsttest","text":""},{"seite":"cvar.html","titel":"20.11 Zusammenfassung","anker":"sec:cvar-zusammenfassung","text":""},{"seite":"handelsmaschine.html","titel":"Kapitel 21: Die vollständige quantitative Handelsmaschine","anker":null,"text":""},{"seite":"handelsmaschine.html","titel":"21.1 In 5 Minuten gelöst","anker":"sec:handelsmaschine-schnellstart","text":""},{"seite":"handelsmaschine.html","titel":"21.2 Lernziele","anker":"sec:handelsmaschine-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"handelsmaschine.html","titel":"21.3 Die Architektur","anker":"sec:handelsmaschine-die-architektur","text":""},{"seite":"handelsmaschine.html","titel":"21.4 Rebalancing-Termine richtig bestimmen","anker":"sec:handelsmaschine-rebalancing-termine-richtig-bestimmen","text":""},{"seite":"handelsmaschine.html","titel":"21.5 Die Engine","anker":"sec:handelsmaschine-die-engine","text":""},{"seite":"handelsmaschine.html","titel":"21.6 Die fünf Selbsttäuschungen des Backtestens","anker":"sec:handelsmaschine-die-fuenf-selbsttaeuschungen-des-backtestens","text":""},{"seite":"handelsmaschine.html","titel":"21.7 Vom Backtest zum Betrieb","anker":"sec:handelsmaschine-vom-backtest-zum-betrieb","text":"Ein guter Backtest ist eine notwendige, keine hinreichende Bedingung. Was zwischen Backtest und echtem Einsatz liegt:"},{"seite":"handelsmaschine.html","titel":"21.8 Übungsaufgaben","anker":"sec:handelsmaschine-uebungsaufgaben","text":""},{"seite":"handelsmaschine.html","titel":"21.9 Finde den Denkfehler","anker":"sec:handelsmaschine-denkfehler","text":"Backtest_Fallen.py zeigt drei Fehler, die im Backtest stecken: Lookahead, Survivorship, vergessene Kosten. Alle drei kann man einem Programm ansehen, wenn man es liest."},{"seite":"handelsmaschine.html","titel":"21.10 Micro-Quiz","anker":"sec:handelsmaschine-quiz","text":""},{"seite":"handelsmaschine.html","titel":"21.11 Selbsttest","anker":"sec:handelsmaschine-selbsttest","text":""},{"seite":"handelsmaschine.html","titel":"21.12 Zusammenfassung","anker":"sec:handelsmaschine-zusammenfassung","text":""},{"seite":"praxisfallen.html","titel":"Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz","anker":null,"text":""},{"seite":"praxisfallen.html","titel":"22.1 In 5 Minuten gelöst","anker":"sec:praxisfallen-schnellstart","text":"Die vorangegangenen vierzehn Kapitel enden fast alle mit derselben Empfehlung: Prüfen Sie das Ergebnis gegen die Wirklichkeit. Dieses Kapitel beginnt damit, diese Prüfung als wiede"},{"seite":"praxisfallen.html","titel":"22.2 Lernziele","anker":"sec:praxisfallen-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"praxisfallen.html","titel":"22.3 Die fünf typischen Praxisfallen","anker":"sec:praxisfallen-die-fuenf-typischen-praxisfallen","text":""},{"seite":"praxisfallen.html","titel":"Falle 1 — Infeasibility","anker":"falle-1-infeasibility","text":"Wenn Parameter unglücklich zusammentreffen — drei Mitarbeitende gleichzeitig krank, kein qualifizierter Ersatz —, meldet der Solver schlicht INFEASIBLE."},{"seite":"praxisfallen.html","titel":"Falle 2 — Der Black-Box-Effekt","anker":"falle-2-der-black-box-effekt","text":"Ein Planer oder Trader lehnt ein System ab, wenn er die Zuweisungen nicht nachvollziehen kann. Akzeptanz ist keine Nebensache, sondern Voraussetzung für den Betrieb."},{"seite":"praxisfallen.html","titel":"Falle 3 — Regimewechsel und Schätzfehler","anker":"falle-3-regimewechsel-und-schätzfehler","text":"Ein Modell, das auf einer mehrjährigen Aufwärtsphase kalibriert wurde, kennt keine Liquiditätskrise und keine Zinswende."},{"seite":"praxisfallen.html","titel":"Falle 4 — Lookahead- und Survivorship-Bias","anker":"falle-4-lookahead--und-survivorship-bias","text":"Ausführlich in Kapitel 21 behandelt. Die Kurzfassung: Beide Fehler machen Backtests systematisch zu gut, ohne eine Fehlermeldung zu erzeugen. Der Lookahead-Selbsttest aus Abschnitt"},{"seite":"praxisfallen.html","titel":"Falle 5 — Laufzeitexplosion","anker":"falle-5-laufzeitexplosion","text":"Exakte Optimalität kann bei MILP oder CP-SAT Minuten bis Stunden dauern — und die Laufzeit wächst nicht linear mit der Problemgröße."},{"seite":"praxisfallen.html","titel":"22.4 Architektur einer produktionsreifen OR-Plattform","anker":"sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform","text":""},{"seite":"praxisfallen.html","titel":"22.5 Der gemeinsame Unterbau: or_kern.py","anker":"sec:praxisfallen-or-kern","text":"Die fünf Prinzipien oben beschreiben eine Plattform. Der Weg dorthin beginnt aber viel kleiner — mit der Frage, welche Teile eines Optimierungsprogramms immer dieselben sind."},{"seite":"praxisfallen.html","titel":"Die vier Schichten","anker":"die-vier-schichten","text":"\\underbrace{\\text{Rohdaten}}_{\\text{Excel, CSV, ERP}}\n\\;\\longrightarrow\\;\n\\underbrace{\\text{Domänenmodell}}_{\\text{geprüft, solverfrei}}\n\\;\\longrightarrow\\;\n\\underbrace{\\text{Model"},{"seite":"praxisfallen.html","titel":"Ein Enum für fünf Bibliotheken","anker":"ein-enum-für-fünf-bibliotheken","text":"Der unscheinbarste, aber wirksamste Teil ist die Statusübersetzung. Dieselbe Aussage heißt in den fünf im Buch verwendeten Bibliotheken:"},{"seite":"praxisfallen.html","titel":"22.6 Der Solverwechsel in der Praxis","anker":"sec:praxisfallen-solverwechsel","text":"Der vorige Abschnitt endet mit einer Behauptung: Ein Solverwechsel betreffe genau einen Baustein. Das ist die Art Satz, die in jedem Architekturvortrag vorkommt und die man deshalb"},{"seite":"praxisfallen.html","titel":"22.7 Eine Checkliste vor dem Produktivgang","anker":"sec:praxisfallen-eine-checkliste-vor-dem-produktivgang","text":"Arbeiten Sie diese Liste ab, bevor ein OR-System in Betrieb geht:"},{"seite":"praxisfallen.html","titel":"22.8 Weiterführende Literatur und Roadmap","anker":"sec:praxisfallen-weiterfuehrende-literatur-und-roadmap","text":"Grundlagen und lineare/ganzzahlige Optimierung * Bertsimas & Tsitsiklis: Introduction to Linear Optimization — Standardwerk zu Simplex, Dualität, Polyedertheorie. * Wolsey: Int"},{"seite":"praxisfallen.html","titel":"22.9 Übungsaufgaben","anker":"sec:praxisfallen-uebungsaufgaben","text":""},{"seite":"praxisfallen.html","titel":"22.10 Finde den Denkfehler","anker":"sec:praxisfallen-denkfehler","text":"Falle 5 — die Laufzeitexplosion — zeigt sich im Betrieb selten als Absturz. Sie zeigt sich als gar nichts."},{"seite":"praxisfallen.html","titel":"22.11 Micro-Quiz","anker":"sec:praxisfallen-quiz","text":""},{"seite":"praxisfallen.html","titel":"22.12 Selbsttest","anker":"sec:praxisfallen-selbsttest","text":""},{"seite":"praxisfallen.html","titel":"22.13 Zusammenfassung","anker":"sec:praxisfallen-zusammenfassung","text":""},{"seite":"testing.html","titel":"Kapitel 23: Testen, Messen, Ausliefern","anker":null,"text":""},{"seite":"testing.html","titel":"23.1 In 5 Minuten gelöst","anker":"sec:testing-schnellstart","text":""},{"seite":"testing.html","titel":"23.2 Lernziele","anker":"sec:testing-lernziele","text":"Nach diesem Kapitel können Sie …"},{"seite":"testing.html","titel":"23.3 Warum Optimierungsmodelle schwer zu testen sind","anker":"sec:testing-warum-schwer","text":"Bei einer gewöhnlichen Funktion schreibt man den erwarteten Wert hin:"},{"seite":"testing.html","titel":"23.4 Die Testsuite","anker":"sec:testing-suite","text":""},{"seite":"testing.html","titel":"23.5 Wer testet die Tests?","anker":"sec:testing-mutation","text":"33 grüne Tests. Das ist noch kein Ergebnis."},{"seite":"testing.html","titel":"Der erste Lauf fand zwei Lücken","anker":"der-erste-lauf-fand-zwei-lücken","text":"Die Suite hatte zunächst 31 Tests. Der Mutationstest sagte dazu:"},{"seite":"testing.html","titel":"23.6 Ein Vergleich, dem man glauben kann","anker":"sec:testing-benchmark","text":"Solververgleiche stehen in jedem Blog, und die meisten sind wertlos. Nicht, weil falsch gemessen wurde, sondern weil zu wenig dazugesagt wird."},{"seite":"testing.html","titel":"Die interessanteste Spalte heißt „Anteil”","anker":"die-interessanteste-spalte-heißt-anteil","text":"Sie sagt, wie viel der Zeit in den Aufbau des Modells geht statt ins Lösen — und die Zahlen sind unbequem:"},{"seite":"testing.html","titel":"23.7 Das Modell als Dienst","anker":"sec:testing-dienst","text":"Ein Modell hinter eine HTTP-Schnittstelle zu hängen sieht nach einer Fingerübung aus. Es gibt aber einen Unterschied, der die ganze Bauform bestimmt:"},{"seite":"testing.html","titel":"Threads oder Prozesse? Eine Messung, keine Meinung","anker":"threads-oder-prozesse-eine-messung-keine-meinung","text":"Punkt 6 des Selbsttests misst, ob vier Solverläufe in Threads sich überlappen. Das Ergebnis ist ein Faktor von rund 3,5 bei vier Threads: OR-Tools rechnet in C++ und gibt den GIL w"},{"seite":"testing.html","titel":"Was im echten Betrieb dazukommt","anker":"was-im-echten-betrieb-dazukommt","text":"Das Beispiel hält das Auftragsbuch in einem dict und rechnet in Threads desselben Prozesses. Für einen einzelnen Rechner reicht das; darüber hinaus nicht:"},{"seite":"testing.html","titel":"23.8 Übungsaufgaben","anker":"sec:testing-uebungsaufgaben","text":""},{"seite":"testing.html","titel":"23.9 Finde den Denkfehler","anker":"sec:testing-denkfehler","text":""},{"seite":"testing.html","titel":"23.10 Micro-Quiz","anker":"sec:testing-quiz","text":""},{"seite":"testing.html","titel":"23.11 Selbsttest","anker":"sec:testing-selbsttest","text":""},{"seite":"testing.html","titel":"23.12 Zusammenfassung","anker":"sec:testing-zusammenfassung","text":""},{"seite":"projektwerkstatt.html","titel":"Projektwerkstatt","anker":null,"text":""},{"seite":"projektwerkstatt.html","titel":"Wie Sie ein Projekt bearbeiten","anker":"wie-sie-ein-projekt-bearbeiten","text":"Halten Sie sich an diese Reihenfolge — sie ist aus vielen gescheiterten und einigen gelungenen Projekten destilliert:"},{"seite":"projektwerkstatt.html","titel":"P1 — Vertretungsplaner für eine Schule","anker":"p1-vertretungsplaner-für-eine-schule","text":""},{"seite":"projektwerkstatt.html","titel":"P2 — Schichtplanung für ein Pflegeteam","anker":"p2-schichtplanung-für-ein-pflegeteam","text":""},{"seite":"projektwerkstatt.html","titel":"P3 — Tourenplanung für einen Lieferdienst","anker":"p3-tourenplanung-für-einen-lieferdienst","text":""},{"seite":"projektwerkstatt.html","titel":"P4 — Standort- und Lagernetzplanung","anker":"p4-standort--und-lagernetzplanung","text":""},{"seite":"projektwerkstatt.html","titel":"P5 — Produktionsplanung mit Rüstkosten","anker":"p5-produktionsplanung-mit-rüstkosten","text":""},{"seite":"projektwerkstatt.html","titel":"P6 — Portfolio-Rebalancer für ein Privatdepot","anker":"p6-portfolio-rebalancer-für-ein-privatdepot","text":""},{"seite":"projektwerkstatt.html","titel":"P7 — Risikoreport mit CVaR und Stresstests","anker":"p7-risikoreport-mit-cvar-und-stresstests","text":""},{"seite":"projektwerkstatt.html","titel":"P9 — Wenn der Solver aussteigt: Tourenplanung in Echtgröße","anker":"p9-wenn-der-solver-aussteigt-tourenplanung-in-echtgröße","text":""},{"seite":"projektwerkstatt.html","titel":"P10 — Zwei Ziele, eine Entscheidung: Kosten gegen CO₂","anker":"p10-zwei-ziele-eine-entscheidung-kosten-gegen-co₂","text":""},{"seite":"projektwerkstatt.html","titel":"P11 — Vom Skript zum Dienst: das Modell übergeben","anker":"p11-vom-skript-zum-dienst-das-modell-übergeben","text":""},{"seite":"projektwerkstatt.html","titel":"P8 — Freies Projekt aus Ihrem eigenen Umfeld","anker":"p8-freies-projekt-aus-ihrem-eigenen-umfeld","text":""},{"seite":"projektwerkstatt.html","titel":"Eine Bitte zum Schluss","anker":"eine-bitte-zum-schluss","text":"Wenn Sie ein Projekt fertig haben, machen Sie zwei Dinge:"},{"seite":"anhang-loesungen.html","titel":"Anhang A: Lösungen zu allen Übungsaufgaben","anker":null,"text":""},{"seite":"anhang-loesungen.html","titel":"A.1 Lösungen zu Kapitel „Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung“","anker":"sec:loesungen-einfuehrung","text":"1.1 — Analytik-Stufen. (a) deskriptiv (Vergangenheit beschreiben). (b) prädiktiv (Prognose). (c) präskriptiv (Handlungsanweisung unter Restriktionen). (d) prädiktiv (Klassifikation"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 1.1 — Die Schreinerei verdoppelt ihren Gewinn","anker":"finde-den-denkfehler-1.1-die-schreinerei-verdoppelt-ihren-gewinn","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-einfuehrung","text":"1 — (c) präskriptiv. Die Frage lautet „was tun“, und es gibt eine Restriktion (das Restbudget). Dass man für die Bewertung eine Prognose braucht, macht die Frage nicht prädiktiv — "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-einfuehrung","text":""},{"seite":"anhang-loesungen.html","titel":"A.2 Lösungen zu Kapitel „Das mathematische Fundament — Vektoren, Matrizen, Konvexität“","anker":"sec:loesungen-fundament","text":"2.1 — Matrixform lesen. \\max 4x_1 + x_2 + 6x_3 u. d. N. x_1 + 2x_2 \\le 10, x_2 + 3x_3 \\le 12, x \\ge 0. 3 Variablen, 2 Nebenbedingungen (plus Nichtnegativität)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 2.1 — Die unauffällige Transposition","anker":"finde-den-denkfehler-2.1-die-unauffällige-transposition","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-fundament","text":"1 — (b). Der Fundamentalsatz sagt nur, dass ein Optimum in einer Ecke angenommen wird. (a) ist falsch, weil es mehrere optimale Ecken geben kann (wenn die Zielfunktion parallel zu "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-fundament","text":""},{"seite":"anhang-loesungen.html","titel":"A.3 Lösungen zu Kapitel „Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten“","anker":"sec:loesungen-oekosystem","text":"3.1 — Solverwahl. (a) CP-SAT (diskrete Zuordnung mit Zeitfenstern). (b) scipy.optimize.linprog (klassisches Mischungs-LP, klein). (c) CVXPY (konvexes QP). (d) MIQP — CVXPY mit Binä"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 3.1 — Der Solver, der angeblich dreimal schneller ist","anker":"finde-den-denkfehler-3.1-der-solver-der-angeblich-dreimal-schneller-ist","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-oekosystem","text":"1 — (b) CP-SAT. Es geht um diskrete Zuweisung mit logischen Regeln („nicht gleichzeitig“) — CP-SATs Kerngebiet, und Regeln dieser Art formuliert man dort direkt statt über Big-M-Um"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-oekosystem","text":""},{"seite":"anhang-loesungen.html","titel":"A.4 Lösungen zu Kapitel „Vom Management-Wunsch zum Modell“","anker":"sec:loesungen-modellierung","text":"Die fehlende Lesart. Eine dritte Lesart: „Der Umsatzanteil der Stammkunden darf nicht unter 30 % fallen.” Sie zielt weder auf einzelne Aufträge noch auf einzelne Kunden, sondern au"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die Engpassmaschine soll zu mindestens 92 % ausgelastet sein“","anker":"denkfehler-loesung-modellierung","text":"Fehler 1: Auslastung ist ein Ergebnis, keine Anforderung."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-modellierung","text":"1. b) Die Zielfunktion ist eine vollständige Aussage darüber, was zählt. Was nicht darin steht, ist für das Modell nicht „weniger wichtig”, sondern wertlos. (a) und (c) sind frei e"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-modellierung","text":""},{"seite":"anhang-loesungen.html","titel":"A.5 Lösungen zu Kapitel „Lineare Programmierung — Simplex, Dualität und Schattenpreise“","anker":"sec:loesungen-lp","text":"4.1 — Schlupf deuten. (a) Ressourcen 1 und 3 (Schlupf 0). (b) Ja: Überall ist s_i \\cdot y_i = 0. (c) In Ressource 3 — der höchste Schattenpreis (9,8) bedeutet den größten Grenznutz"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 4.1 — Die 380 000-Euro-Maschine","anker":"finde-den-denkfehler-4.1-die-380-000-euro-maschine","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-lp","text":"1 — (b). Genau der Satz vom komplementären Schlupf: s_i \\cdot y_i = 0. Ist Schlupf vorhanden, muss der Dualwert null sein. (a) hat die Aussage in ihr Gegenteil verkehrt; (c) verwec"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-lp","text":""},{"seite":"anhang-loesungen.html","titel":"A.6 Lösungen zu Kapitel „Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound“","anker":"sec:loesungen-milp","text":"5.1 — Runden widerlegen. Beispiel: \\max x_1 + x_2 u. d. N. 10x_1 + 10x_2 \\le 15, ganzzahlig. LP-Optimum Z = 1{,}5; Abrunden ergibt (0,0) mit Z = 0 — 100 % Verlust. Das ganzzahlige "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 5.1 — Elf Lager, die keine Fixkosten kosten","anker":"finde-den-denkfehler-5.1-elf-lager-die-keine-fixkosten-kosten","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-milp","text":"1 — (b). Der Gap (48\\,200 - 47\\,100)/48\\,200 = 2{,}3\\,\\% ist eine Garantie, keine Schätzung: Besser als 47 100 € kann es nachweislich nicht werden, also ist der vorliegende Plan hö"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-milp","text":""},{"seite":"anhang-loesungen.html","titel":"A.7 Lösungen zu Kapitel „Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung“","anker":"sec:loesungen-cpsat","text":"6.1 — Propagation. Aus x_1 + x_2 = 8 und x_1 < x_2 folgt x_1 < 4, also x_1 \\in \\{2,3\\} (denn x_2 = 8-x_1 \\le 6 verlangt x_1 \\ge 2) und entsprechend x_2 \\in \\{5,6\\}. Aus 36 Ko"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 6.1 — Die Betriebsvereinbarung, die niemanden interessiert","anker":"finde-den-denkfehler-6.1-die-betriebsvereinbarung-die-niemanden-interessiert","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-cpsat","text":"1 — (b) wirkungslos. INFEASIBLE ist kein Abbruch, sondern ein Beweis: CP-SAT hat gezeigt, dass keine zulässige Lösung existiert. Mehr Zeit (a) oder mehr Arbeiter (c) ändern daran n"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-cpsat","text":""},{"seite":"anhang-loesungen.html","titel":"A.8 Lösungen zu Kapitel „Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP“","anker":"sec:loesungen-graphen","text":"7.1 — Flusserhaltung. b_i = (15+3) - (12+8) = -2 → Senke (Nettobedarf 2)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 7.1 — Die vergessene Dimension","anker":"finde-den-denkfehler-7.1-die-vergessene-dimension","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-graphen","text":"1 — (b) totale Unimodularität. Jede quadratische Teilmatrix hat Determinante 0, +1 oder -1; bei ganzzahliger rechter Seite sind deshalb alle Ecken des zulässigen Bereichs ganzzahli"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-graphen","text":""},{"seite":"anhang-loesungen.html","titel":"A.9 Lösungen zu Kapitel „Metaheuristiken — wenn der exakte Solver aussteigt“","anker":"sec:loesungen-metaheuristiken","text":"Die Kurzsichtigkeit von Hand. Ab Schwarz wählt die Faustregel: Schwarz → Dunkelrot (26) → Rot (5) → … und muss dann in die helle Gruppe: Rot → Weiß (50) → Elfenbein (2) → Beige (2)"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Unsere Heuristik ist 6 % besser“","anker":"denkfehler-loesung-metaheuristiken","text":"Es fehlt die Schranke."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-metaheuristiken","text":"1. b) Eine lokale Suche lebt von der Zahl geprüfter Züge. Die volle Summe kostet O(n) statt O(1) — bei 500 Aufträgen zwei Größenordnungen weniger Züge im selben Zeitbudget. Numeris"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-metaheuristiken","text":""},{"seite":"anhang-loesungen.html","titel":"A.10 Lösungen zu Kapitel „Spaltengenerierung“","anker":"sec:loesungen-dekomposition","text":"Das Abbruchkriterium. Die Eins ist der Zielfunktionskoeffizient eines Musters: Jedes geschnittene Muster verbraucht genau eine Mutterrolle, und die Zielfunktion lautet \\min \\sum_p "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die LP-Lösung sagt 72,92 — also runden wir auf“","anker":"denkfehler-loesung-dekomposition","text":"Das Aufrunden kostet sechs Rollen — acht Prozent."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-dekomposition","text":"1. b) Das Mustermodell kennt keine einzelnen Rollen und damit keine austauschbaren Objekte — die Symmetrie verschwindet. (a) trifft zufällig auch zu, ist aber nicht der Grund; (c) "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-dekomposition","text":""},{"seite":"anhang-loesungen.html","titel":"A.11 Lösungen zu Kapitel „Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität“","anker":"sec:loesungen-qp-nlp","text":"8.1 — Konvexität einordnen. PSD (alle \\ge 0) → konvex, aber nicht streng konvex; die Lösung ist nicht notwendig eindeutig. Der Eigenwert 0 bedeutet eine flache Richtung: Entlang de"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 8.1 — Die Kovarianzmatrix aus dem Controlling","anker":"finde-den-denkfehler-8.1-die-kovarianzmatrix-aus-dem-controlling","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-qp-nlp","text":"1 — (b). Der DCPError ist eine inhaltliche Aussage: Für diese Formulierung kann es keine Optimalitätsgarantie geben. Auf scipy auszuweichen (a) beseitigt die Meldung, nicht die Urs"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-qp-nlp","text":""},{"seite":"anhang-loesungen.html","titel":"A.12 Lösungen zu Kapitel „Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit“","anker":"sec:loesungen-unsicherheit","text":"9.1 — Fluch des Durchschnitts. Weil sich Verzögerungen fortpflanzen und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (d"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 9.1 — Warum jedes Projekt zu spät fertig wird","anker":"finde-den-denkfehler-9.1-warum-jedes-projekt-zu-spät-fertig-wird","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-unsicherheit","text":"1 — (b). Das kritische Verhältnis 1400/(1400+120) = 0{,}921 gibt an, wie weit man sich auf die günstigere Fehlerseite stellen soll: Bestellt wird das 92,1-%-Quantil des Bedarfs, hi"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-unsicherheit","text":""},{"seite":"anhang-loesungen.html","titel":"A.13 Lösungen zu Kapitel „Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution“","anker":"sec:loesungen-dynamische-programmierung","text":"10.1 — Bausteine. Stufe = Tag 1–5; Zustand = verbleibende Distanz (ggf. plus Erschöpfungsgrad); Aktion = heutige Tagesetappe; Wertfunktion = minimale Restanstrengung. Nicht in den "},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 10.1 — Der Zustand, der zu wenig weiß","anker":"finde-den-denkfehler-10.1-der-zustand-der-zu-wenig-weiß","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-dynamische-programmierung","text":"1 — (b). V_t setzt V_{t+1} voraus: Eine Entscheidung lässt sich erst bewerten, wenn feststeht, was sie für die Zukunft bedeutet. Am Ende ist dieser Wert bekannt (V_T = g), deshalb "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-dynamische-programmierung","text":""},{"seite":"anhang-loesungen.html","titel":"A.14 Lösungen zu Kapitel „Mehrere Ziele — Pareto-Fronten statt Gewichte“","anker":"sec:loesungen-mehrziel","text":"Dominanz prüfen. P (14 000 €, 6 000 kg) und Q (13 800 €, 6 100 kg): keiner dominiert den anderen. Q ist billiger, P ist sauberer — beide sind pareto-optimal, die Wahl ist eine Präf"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir haben die Gewichte sauber kalibriert“","anker":"denkfehler-loesung-mehrziel","text":"Der Lösungsraum wurde nicht vollständig abgetastet — und zwar aus prinzipiellen Gründen."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-mehrziel","text":"1. b) Geometrisch heißt „gewichtete Summe minimieren“: eine Gerade von links unten an die Punktwolke schieben. Sie berührt immer einen Eckpunkt der unteren konvexen Hülle; Punkte d"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-mehrziel","text":""},{"seite":"anhang-loesungen.html","titel":"A.15 Lösungen zu Kapitel „Predict-then-Optimize“","anker":"sec:loesungen-prognose","text":"Andere Preise. Der Überhang kostet nur noch 1,50 €, die Fehlmenge weiterhin 6 €. Das kritische Verhältnis steigt von 6/9 = 0{,}667 auf 6/7{,}5 = 0{,}80. Die Bestellmenge verschiebt"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir haben die Prognose um 18 % verbessert“","anker":"denkfehler-loesung-prognose","text":"Der Sicherheitszuschlag stammt aus den Residuen des alten Modells."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-prognose","text":"1. b) Die Kostenasymmetrie verschiebt das Optimum vom Erwartungswert zum kritischen Quantil. (a) und (c) sind reale Probleme, aber nicht dieses: Selbst bei perfekt bekannter, norma"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-prognose","text":""},{"seite":"anhang-loesungen.html","titel":"A.16 Lösungen zu Kapitel „Die Strukturbrücke — dieselbe Mathematik, zwei Welten“","anker":"sec:loesungen-bruecke","text":"Die dritte Ressource. „Liquidität” — wie schnell sich eine Position ohne Kursabschlag verkaufen lässt — entspricht in der Werkstatt der Umrüstbarkeit oder der Vorlaufzeit: Wie schn"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Das ist doch dasselbe Problem“","anker":"denkfehler-loesung-bruecke","text":"Fehler 1: Varianz ist hier das falsche Risikomaß — genau der Fall, für den es CVaR gibt."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-bruecke","text":"1. b) Beide Probleme haben dieselbe Struktur: knappe Größen auf konkurrierende Verwendungen verteilen. (a) ist falsch — Produktionsproblem ist streng typisiert und validiert beim E"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-bruecke","text":""},{"seite":"anhang-loesungen.html","titel":"A.17 Lösungen zu Kapitel „Supply-Chain und Energieeinsatz unter Unsicherheit“","anker":"sec:loesungen-supplychain","text":"Der Umschlagpunkt. Bei 60 MW statt 120 MW halbiert sich die Grenzkostendifferenz je Stunde (83 €/MWh × 60 MW = 4 980 €/h). Der Umschlagpunkt verdoppelt sich auf 7,7 Stunden."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Wir rechnen mit dem P50-Szenario“","anker":"denkfehler-loesung-supplychain","text":"Fehler 1: Der Median ist keine Absicherung, sondern eine Wette auf die Hälfte der Fälle."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-supplychain","text":"1. b) Anfahrkosten und Mindestlaufzeiten koppeln die Stunden. Die Merit-Order ist eine Sortierung und kennt keine Kopplung über die Zeit; sie ist für einen einzelnen Zeitpunkt rich"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-supplychain","text":""},{"seite":"anhang-loesungen.html","titel":"A.18 Lösungen zu Kapitel „Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage“","anker":"sec:loesungen-finanzdaten","text":"11.1 — Renditeart. (a) diskret. (b) logarithmisch. (c) logarithmisch (bzw. diskret kumuliert über Produkt). (d) logarithmisch (statistische Eigenschaften)."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 11.1 — Das risikofreie Portfolio","anker":"finde-den-denkfehler-11.1-das-risikofreie-portfolio","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-finanzdaten","text":"1 — (b) bei 75 %. 1{,}0 \\cdot 0{,}5 \\cdot 1{,}5 = 0{,}75. Prozentuale Änderungen verknüpfen sich multiplikativ; ein Verlust wiegt schwerer als ein gleich großer Gewinn, weil er von"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-finanzdaten","text":""},{"seite":"anhang-loesungen.html","titel":"A.19 Lösungen zu Kapitel „Die moderne Portfoliotheorie nach Markowitz“","anker":"sec:loesungen-markowitz","text":"12.1 — Diversifikationseffekt."},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 12.1 — Zwölf gleiche Anlagen, ein sehr ungleiches Portfolio","anker":"finde-den-denkfehler-12.1-zwölf-gleiche-anlagen-ein-sehr-ungleiches-portfolio","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-markowitz","text":"1 — (b). Das renditestärkste Portfolio auf der Effizienzlinie hat notwendig das schlechteste Rendite-Risiko-Verhältnis (im Kapitelbeispiel 0,44 gegenüber 0,75 beim Optimum) und bes"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-markowitz","text":""},{"seite":"anhang-loesungen.html","titel":"A.20 Lösungen zu Kapitel „Tail-Risiko, CVaR und Transaktionskosten“","anker":"sec:loesungen-cvar","text":"13.1 — VaR und CVaR von Hand. Sortierte Verluste: -2{,}0; -1{,}2; -0{,}8; -0{,}4; 0{,}3; 0{,}6; 1{,}1; 2{,}1; 5{,}4; 8{,}9. Das 80 %-Quantil ist der 8. Wert: \\text{VaR}_{80\\%} = 2{"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 13.1 — Das Risikobudget, das durch Diversifikation stieg","anker":"finde-den-denkfehler-13.1-das-risikobudget-das-durch-diversifikation-stieg","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-cvar","text":"1 — (b) nichts. Der VaR ist ein Quantil: Er markiert die Schwelle und sagt nichts über den Bereich dahinter. Genau das zeigt der Schnellstart dieses Kapitels — zwei Anlagen mit ide"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-cvar","text":""},{"seite":"anhang-loesungen.html","titel":"A.21 Lösungen zu Kapitel „Die vollständige quantitative Handelsmaschine“","anker":"sec:loesungen-handelsmaschine","text":"14.1 — Lookahead erkennen. (a) sauber (:heute). (b) Lookahead — gesamter Zeitraum. (c) Lookahead — Volatilität über den ganzen Zeitraum enthält Zukunft. (d) sauber — Gewichte aus D"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 14.1 — Die Strategie mit dem hochsignifikanten Ergebnis","anker":"finde-den-denkfehler-14.1-die-strategie-mit-dem-hochsignifikanten-ergebnis","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-handelsmaschine","text":"1 — (b) nichts. Beide Kennzahlen sind Mittelwerte über die Zeit und blind für die Reihenfolge. Der Schnellstart dieses Kapitels zeigt es an denselben Tagesrenditen, nur anders ange"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-handelsmaschine","text":""},{"seite":"anhang-loesungen.html","titel":"A.22 Lösungen zu Kapitel „Praxisfallen und der Weg zum produktiven Einsatz“","anker":"sec:loesungen-praxisfallen","text":"15.1 — Hart oder weich, revisited. (a) hart: gesetzliche Ruhezeit, Qualifikationspflicht. (b) weich: individuelle Wunschtage, Vermeidung von Freistunden. (c) diskutabel: Höchstzahl"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler 15.1 — Das Modell, das seit einem Jahr nicht mehr optimiert","anker":"finde-den-denkfehler-15.1-das-modell-das-seit-einem-jahr-nicht-mehr-optimiert","text":""},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-praxisfallen","text":"1 — (b) das Zeitlimit greift. Eine Laufzeit, die exakt dem Limit entspricht und über Monate konstant bleibt, ist kein Stabilitätsbeleg, sondern zeigt, dass der Solver jedes Mal abg"},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-praxisfallen","text":""},{"seite":"anhang-loesungen.html","titel":"A.23 Lösungen zu Kapitel „Testen, Messen, Ausliefern“","anker":"sec:loesungen-testing","text":"Den Schnellstart reparieren. Zwei Wege: (a) Abrunden statt runden — np.floor statt np.round. Das ist immer zulässig, weil weniger produzieren nie eine Kapazität sprengt, kostet abe"},{"seite":"anhang-loesungen.html","titel":"Finde den Denkfehler — „Die Suite ist grün, das Modell stimmt”","anker":"denkfehler-loesung-testing","text":"Alle Prüfungen benutzen dieselbe falsche Zahl."},{"seite":"anhang-loesungen.html","titel":"Micro-Quiz","anker":"quiz-loesung-testing","text":"1. b) Es gibt keine unabhängige Quelle für die richtige Antwort — sonst bräuchte man den Solver nicht. (a) ist bei festgelegtem Seed und Zeitlimit meist beherrschbar; (c) löst man "},{"seite":"anhang-loesungen.html","titel":"Selbsttest","anker":"selbsttest-loesung-testing","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Anhang B: Katalog der Modellierungsmuster","anker":null,"text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Übersicht","anker":"übersicht","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"Logische Schalter","anker":"logische-schalter","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B1 — Aktivierungsschalter (Fixkosten)","anker":"b1-aktivierungsschalter-fixkosten","text":"Regel. „Wenn überhaupt etwas produziert wird, fallen Rüstkosten F an.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B2 — Semikontinuierliche Variable","anker":"b2-semikontinuierliche-variable","text":"Regel. „Entweder gar nicht oder mindestens L (und höchstens U).“"},{"seite":"anhang-modellierungsmuster.html","titel":"B3 — Implikation","anker":"b3-implikation","text":"Regel. „Wenn A gewählt wird, muss auch B gewählt werden.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B4 — Entweder-Oder (disjunktive Bedingung)","anker":"b4-entweder-oder-disjunktive-bedingung","text":"Regel. „Es muss f(\\mathbf{x}) \\le b_1 oder g(\\mathbf{x}) \\le b_2 gelten.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B5 — Exklusiv-Oder","anker":"b5-exklusiv-oder","text":"Regel. „Genau eine der Optionen wird gewählt.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B6 — Kardinalität","anker":"b6-kardinalität","text":"Regel. „Höchstens (mindestens, genau) K von N.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B7 — Bedingte Kopplung (Konjunktion)","anker":"b7-bedingte-kopplung-konjunktion","text":"Regel. „Wenn A und B, dann auch C.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Mengen und Grenzen","anker":"mengen-und-grenzen","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B8 — Weiche Grenze mit Strafkosten","anker":"b8-weiche-grenze-mit-strafkosten","text":"Regel. „Möglichst nicht über b — wenn doch, kostet es.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B9 — Gestaffelte Preise (stückweise linear)","anker":"b9-gestaffelte-preise-stückweise-linear","text":"Regel. „Die ersten 100 Stück kosten 5 €, danach 8 €.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B10 — Absolutbetrag / Abweichung","anker":"b10-absolutbetrag-abweichung","text":"Regel. „Die Abweichung vom Zielwert soll klein sein, egal in welche Richtung.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B11 — Min/Max in der Zielfunktion","anker":"b11-minmax-in-der-zielfunktion","text":"Regel. „Der am schlechtesten gestellte Beteiligte soll möglichst gut dastehen“ (Maximin/Fairness)."},{"seite":"anhang-modellierungsmuster.html","titel":"B12 — Verhältnis-Bedingung","anker":"b12-verhältnis-bedingung","text":"Regel. „Der Anteil von Gruppe G soll mindestens 30 % betragen.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B25 — Mindestabnahmemenge über einen Zeitraum","anker":"b25-mindestabnahmemenge-über-einen-zeitraum","text":"Regel. „Entweder wir arbeiten mit diesem Lieferanten gar nicht, oder wir nehmen ihm im Jahr mindestens 500 Einheiten ab.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B27 — Budgetlimit","anker":"b27-budgetlimit","text":"Regel. „Alle Maßnahmen zusammen dürfen 2 Mio. € nicht überschreiten.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Zeit und Reihenfolge","anker":"zeit-und-reihenfolge","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B13 — Vorrangbeziehung","anker":"b13-vorrangbeziehung","text":"Regel. „Arbeitsgang B darf erst beginnen, wenn A fertig ist.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B14 — Nichtüberlappung","anker":"b14-nichtüberlappung","text":"Regel. „Eine Maschine bearbeitet nur einen Job gleichzeitig.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B15 — Kumulative Ressource","anker":"b15-kumulative-ressource","text":"Regel. „Zu keinem Zeitpunkt dürfen mehr als 3 Arbeiten gleichzeitig laufen“ bzw. „die Stromlast darf 500 kW nie übersteigen“."},{"seite":"anhang-modellierungsmuster.html","titel":"B16 — Gleitendes Fenster","anker":"b16-gleitendes-fenster","text":"Regel. „Höchstens 5 Arbeitstage in Folge“ / „mindestens 2 freie Tage je Woche“."},{"seite":"anhang-modellierungsmuster.html","titel":"B17 — Umrüst- bzw. Wechselkosten","anker":"b17-umrüst--bzw.-wechselkosten","text":"Regel. „Ein Produktwechsel kostet Rüstzeit.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B26 — Rüstzeit als Kapazitätsverbrauch","anker":"b26-rüstzeit-als-kapazitätsverbrauch","text":"Regel. „Das Umrüsten kostet nicht nur Geld, es kostet Maschinenstunden — und die fehlen dann für die Produktion.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Robustheit und Diagnose","anker":"robustheit-und-diagnose","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B18 — Schlupfvariablen gegen Unlösbarkeit","anker":"b18-schlupfvariablen-gegen-unlösbarkeit","text":"Das wichtigste Muster für den Produktivbetrieb."},{"seite":"anhang-modellierungsmuster.html","titel":"B19 — Hierarchische Ziele (lexikografisch)","anker":"b19-hierarchische-ziele-lexikografisch","text":"Regel. „Erst die Besetzung sicherstellen, dann die Fairness optimieren.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B20 — Symmetriebrechung","anker":"b20-symmetriebrechung","text":"Regel. „Drei identische Maschinen — der Solver soll nicht alle Vertauschungen durchprobieren.“"},{"seite":"anhang-modellierungsmuster.html","titel":"B21 — Worst-Case-Abzug (robuste Formulierung)","anker":"b21-worst-case-abzug-robuste-formulierung","text":"Regel. „Rechne nicht mit dem geschätzten Wert, sondern mit dem ungünstigsten plausiblen.“"},{"seite":"anhang-modellierungsmuster.html","titel":"Netzwerke","anker":"netzwerke","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B22 — Flusserhaltung","anker":"b22-flusserhaltung","text":"\\sum_{j:(i,j)\\in E} x_{ij} - \\sum_{k:(k,i)\\in E} x_{ki} = b_i \\qquad \\forall i"},{"seite":"anhang-modellierungsmuster.html","titel":"B23 — Zuordnung 1:1","anker":"b23-zuordnung-11","text":""},{"seite":"anhang-modellierungsmuster.html","titel":"B24 — Subtour-Eliminierung","anker":"b24-subtour-eliminierung","text":"MTZ (einfach, aber schwach): u_i - u_j + C x_{ij} \\le C - d_j \\qquad \\forall i\\ne j"},{"seite":"anhang-modellierungsmuster.html","titel":"Ein Wort zur Auswahl","anker":"ein-wort-zur-auswahl","text":"Wenn mehrere Muster passen, entscheiden Sie nach dieser Reihenfolge:"},{"seite":"anhang-fehlerdiagnose.html","titel":"Anhang C: Fehlerdiagnose-Handbuch","anker":null,"text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Symptom-Schnellübersicht","anker":"symptom-schnellübersicht","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C1 — INFEASIBLE","anker":"c1-infeasible","text":"Bedeutung. Es gibt keinen einzigen Punkt, der alle Bedingungen gleichzeitig erfüllt. Das ist eine Aussage über Ihr Modell, nicht über den Solver."},{"seite":"anhang-fehlerdiagnose.html","titel":"Diagnose in fünf Schritten","anker":"diagnose-in-fünf-schritten","text":"Schritt 1 — Trivialprüfungen von Hand. Rechnen Sie die offensichtlichen Bilanzen nach, bevor Sie den Solver befragen:"},{"seite":"anhang-fehlerdiagnose.html","titel":"Wenn die fünf Schritte nicht reichen: den Konflikt einkreisen","anker":"wenn-die-fünf-schritte-nicht-reichen-den-konflikt-einkreisen","text":"Warum Schritt 2 so oft ins Leere läuft. Bedingungen einzeln abzuschalten funktioniert nur, solange es einen Schuldigen gibt. Überlagern sich zwei unabhängige Widersprüche, bleibt d"},{"seite":"anhang-fehlerdiagnose.html","titel":"C2 — UNBOUNDED","anker":"c2-unbounded","text":"Bedeutung. Die Zielfunktion lässt sich unbegrenzt verbessern."},{"seite":"anhang-fehlerdiagnose.html","titel":"C3 — Zu langsam","anker":"c3-zu-langsam","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Diagnosereihenfolge","anker":"diagnosereihenfolge","text":"1. Ist es überhaupt das Lösen?"},{"seite":"anhang-fehlerdiagnose.html","titel":"C4 — Unsinniges Ergebnis","anker":"c4-unsinniges-ergebnis","text":"Der Solver hat immer recht — bezogen auf das Modell, das Sie ihm gegeben haben. Wenn das Ergebnis unsinnig ist, beschreibt Ihr Modell nicht das Problem, das Sie meinen."},{"seite":"anhang-fehlerdiagnose.html","titel":"Checkliste","anker":"checkliste","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"Die wirksamste Gegenmaßnahme","anker":"die-wirksamste-gegenmaßnahme","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C5 — Instabile Lösung","anker":"c5-instabile-lösung","text":"Symptom. Ein zusätzlicher Handelstag, und das Portfolio sieht völlig anders aus."},{"seite":"anhang-fehlerdiagnose.html","titel":"Derselbe Fehler außerhalb der Finanzwelt","anker":"derselbe-fehler-außerhalb-der-finanzwelt","text":"Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^"},{"seite":"anhang-fehlerdiagnose.html","titel":"C6 — Falsche Dualwerte","anker":"c6-falsche-dualwerte","text":"Symptom. Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource."},{"seite":"anhang-fehlerdiagnose.html","titel":"Die drei Prüfungen","anker":"die-drei-prüfungen","text":"1. Vorzeichenkonvention. Haben Sie zur Maximierung negiert?"},{"seite":"anhang-fehlerdiagnose.html","titel":"C7 — Widersprüchliche Solver","anker":"c7-widersprüchliche-solver","text":"Symptom. linprog sagt 530, CVXPY sagt 529,8."},{"seite":"anhang-fehlerdiagnose.html","titel":"C8 — DCPError","anker":"c8-dcperror","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C9 — Importfehler","anker":"c9-importfehler","text":""},{"seite":"anhang-fehlerdiagnose.html","titel":"C10 — Verdächtig guter Backtest","anker":"c10-verdächtig-guter-backtest","text":"Faustregel: Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund."},{"seite":"anhang-fehlerdiagnose.html","titel":"Prüfreihenfolge","anker":"prüfreihenfolge","text":"1. Lookahead-Selbsttest."},{"seite":"anhang-fehlerdiagnose.html","titel":"C11 — Vertauschte Spalten","anker":"c11-vertauschte-spalten","text":"Symptom. Ergebnisse sind plausibel, aber falsch beschriftet — oder eine Sektorgrenze greift auf die falschen Titel."},{"seite":"anhang-fehlerdiagnose.html","titel":"Die goldene Diagnoseregel","anker":"die-goldene-diagnoseregel","text":""},{"seite":"anhang-spickzettel.html","titel":"Anhang D: Spickzettel der Solver","anker":null,"text":""},{"seite":"anhang-spickzettel.html","titel":"D1 — SciPy: linprog und milp","anker":"d1-scipy-linprog-und-milp","text":"Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nic"},{"seite":"anhang-spickzettel.html","titel":"Lineares Programm","anker":"lineares-programm","text":""},{"seite":"anhang-spickzettel.html","titel":"Ganzzahlig: milp","anker":"ganzzahlig-milp","text":""},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine","text":""},{"seite":"anhang-spickzettel.html","titel":"D2 — HiGHS über highspy","anker":"d2-highs-über-highspy","text":"Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-1","text":""},{"seite":"anhang-spickzettel.html","titel":"D3 — OR-Tools: pywraplp","anker":"d3-or-tools-pywraplp","text":"Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht: Scheduling und kombinatorische Bedingungen — dafür ist CP-SA"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-2","text":""},{"seite":"anhang-spickzettel.html","titel":"D4 — CP-SAT: cp_model","anker":"d4-cp-sat-cp_model","text":"Wofür. Scheduling, Zuordnung, Reihenfolgen, alles Kombinatorische mit globalen Bedingungen. Wofür nicht: stetige Größen — CP-SAT rechnet ausschließlich ganzzahlig. Wer Nachkommaste"},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-3","text":""},{"seite":"anhang-spickzettel.html","titel":"D5 — CVXPY","anker":"d5-cvxpy","text":"Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst "},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-4","text":""},{"seite":"anhang-spickzettel.html","titel":"D6 — Modellierungssprachen: Pyomo und Linopy","anker":"d6-modellierungssprachen-pyomo-und-linopy","text":"Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy "},{"seite":"anhang-spickzettel.html","titel":"Pyomo","anker":"pyomo","text":""},{"seite":"anhang-spickzettel.html","titel":"Linopy","anker":"linopy","text":""},{"seite":"anhang-spickzettel.html","titel":"Die drei häufigsten Stolpersteine","anker":"die-drei-häufigsten-stolpersteine-5","text":""},{"seite":"anhang-spickzettel.html","titel":"Die gemeinsame Regel","anker":"die-gemeinsame-regel","text":"Alle sechs Seiten haben denselben längsten Abschnitt: die Statusauswertung. Das ist kein Zufall. Ein Solveraufruf hat nie zwei Ausgänge, sondern mindestens fünf — optimal, zulässig"},{"seite":"anhang-glossar-literatur.html","titel":"Anhang E: Glossar und Literatur","anker":null,"text":""},{"seite":"anhang-glossar-literatur.html","titel":"Mathematische Optimierung — Grundlagen","anker":"mathematische-optimierung-grundlagen","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Konvexe, nichtlineare und robuste Optimierung","anker":"konvexe-nichtlineare-und-robuste-optimierung","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Constraint Programming und Scheduling","anker":"constraint-programming-und-scheduling","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Quantitative Finanzmathematik","anker":"quantitative-finanzmathematik","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Software und Dokumentation","anker":"software-und-dokumentation","text":""},{"seite":"anhang-glossar-literatur.html","titel":"Verbände und Normen","anker":"verbände-und-normen","text":""}]; diff --git a/OR_HTML_04/gesamtdokument.html b/OR_HTML_04/gesamtdokument.html index e68297e..dca8759 100644 --- a/OR_HTML_04/gesamtdokument.html +++ b/OR_HTML_04/gesamtdokument.html @@ -742,16 +742,22 @@
  • 12.2 Lernziele
  • 12.3 Der Fluch des Durchschnitts
  • 12.4 Monte-Carlo-Simulation
  • 12.5 Zweistufige stochastische Programmierung
  • 12.6 Robuste Optimierung: gegen den Worst Case absichern
  • -
  • 12.7 Übungsaufgaben
  • -
  • 12.8 Finde den Denkfehler
  • -
  • 12.9 Micro-Quiz
  • -
  • 12.10 Selbsttest
  • -
  • 12.11 Zusammenfassung
  • +
  • 12.7 Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“ +
  • +
  • 12.8 Übungsaufgaben
  • +
  • 12.9 Finde den Denkfehler
  • +
  • 12.10 Micro-Quiz
  • +
  • 12.11 Selbsttest
  • +
  • 12.12 Zusammenfassung
  • Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution

    Die wirksamste Gegenmaßnahme

    -
    # Nach JEDEM Lösen: alle Regeln explizit nachprüfen
    -assert abs(w.sum() - 1) < 1e-6,       "Budget nicht eingehalten"
    -assert w.min() > -1e-9,               "negatives Gewicht"
    -assert w.max() <= grenze + 1e-9,      "Positionsgrenze verletzt"
    -for sektor, idx in sektoren.items():
    -    assert w[idx].sum() <= limit[sektor] + 1e-9, f"Sektor {sektor} verletzt"
    +
    # Nach JEDEM Lösen: alle Regeln explizit nachprüfen
    +assert abs(w.sum() - 1) < 1e-6,       "Budget nicht eingehalten"
    +assert w.min() > -1e-9,               "negatives Gewicht"
    +assert w.max() <= grenze + 1e-9,      "Positionsgrenze verletzt"
    +for sektor, idx in sektoren.items():
    +    assert w[idx].sum() <= limit[sektor] + 1e-9, f"Sektor {sektor} verletzt"

    Diese Zeilen kosten Sekunden und fangen die Fehlerklasse ab, die der Solver nicht melden kann: dass Ihr Modell etwas anderes beschreibt, als Sie glauben.


    C5 — Instabile Lösung

    @@ -30321,37 +30692,37 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose. -
    eig = np.linalg.eigvalsh(Sigma)
    -print(f"Kondition {eig.max()/eig.min():.0f} | kleinster EW {eig.min():.2e}")
    -# Faustregel: Kondition > 1000 ist bedenklich, > 10000 kritisch
    +
    eig = np.linalg.eigvalsh(Sigma)
    +print(f"Kondition {eig.max()/eig.min():.0f} | kleinster EW {eig.min():.2e}")
    +# Faustregel: Kondition > 1000 ist bedenklich, > 10000 kritisch

    Derselbe Fehler außerhalb der Finanzwelt

    Instabilität ist kein Portfolioproblem, sondern ein Skalierungsproblem — und es trifft jedes Modell, in dem Größen sehr verschiedener Ordnung nebeneinanderstehen. Euro-Beträge (10^7) und Tonnen-Angaben (10^{-3}) in derselben Matrix erzeugen eine Konditionszahl, die aus einer Datenunsicherheit von 0,1 % eine Lösungsänderung von 100 % machen kann. Die Konditionszahl ist genau die Obergrenze dieses Verstärkungsfaktors.

    -
    print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")
    -# Grobe Peilung: Wertebereiche der Koeffizienten anschauen
    -print(f"kleinster / groesster Betrag: {np.abs(A[A != 0]).min():.1e} "
    -      f"/ {np.abs(A).max():.1e}")
    +
    print(f"Kondition der Restriktionsmatrix: {np.linalg.cond(A):.1e}")
    +# Grobe Peilung: Wertebereiche der Koeffizienten anschauen
    +print(f"kleinster / groesster Betrag: {np.abs(A[A != 0]).min():.1e} "
    +      f"/ {np.abs(A).max():.1e}")

    Klaffen die Beträge um mehr als drei, vier Größenordnungen auseinander, rechnen Sie in anderen Einheiten (Tausend Euro statt Euro, Kilogramm statt Tonnen) oder skalieren Sie Zeilen und Spalten automatisch. Skalierung_Kondition.py zeigt beides samt Ruiz-Equilibrierung (Abschnitt 2.7).


    C6 — Falsche Dualwerte

    Symptom. Schattenpreise sind negativ, wo sie positiv sein sollten — oder null bei einer offensichtlich knappen Ressource.

    Die drei Prüfungen

    1. Vorzeichenkonvention. Haben Sie zur Maximierung negiert?

    -
    schattenpreise = -res.ineqlin.marginals    # bei linprog nach Negation
    +
    schattenpreise = -res.ineqlin.marginals    # bei linprog nach Negation

    Genau das ist die Vorzeichenfalle aus Abschnitt 5.7.

    2. Komplementärer Schlupf.

    -
    for s, y in zip(res.slack, schattenpreise):
    -    assert abs(s * y) < 1e-6, "Komplementaerer Schlupf verletzt!"
    +
    for s, y in zip(res.slack, schattenpreise):
    +    assert abs(s * y) < 1e-6, "Komplementaerer Schlupf verletzt!"

    Eine Ressource mit Reserve muss Schattenpreis 0 haben.

    3. Numerische Gegenprobe — der zuverlässigste Test.

    -
    b_plus = b.copy(); b_plus[i] += 1.0
    -zuwachs = loese(b_plus) - loese(b)
    -assert abs(zuwachs - schattenpreise[i]) < 1e-6
    +
    b_plus = b.copy(); b_plus[i] += 1.0
    +zuwachs = loese(b_plus) - loese(b)
    +assert abs(zuwachs - schattenpreise[i]) < 1e-6

    Dieser Test ist unabhängig von jeder Vorzeichenkonvention. Nutzen Sie ihn.

    4. Wenn alle drei Prüfungen bestehen und der Wert trotzdem seltsam ist: Entartung. Die drei Prüfungen oben setzen voraus, dass es einen richtigen Schattenpreis gibt. Bei einem entarteten Optimum — mehr aktive Nebenbedingungen als Variablen — gibt es den nicht. Der Wert ist dann nicht falsch, sondern mehrdeutig: zwei korrekte Solver liefern für dasselbe Optimum verschiedene Dualwerte, und die numerische Gegenprobe aus Schritt 3 schlägt fehl, weil der Zuwachs nach links und nach rechts unterschiedlich ausfällt.

    -
    aktiv = int(np.sum(res.slack < 1e-9))          # wie viele Bedingungen sind straff?
    -if aktiv > len(res.x):
    -    print(f"Entartet: {aktiv} aktive Bedingungen bei {len(res.x)} Variablen.")
    -    print("Einen einzelnen Schattenpreis zu berichten waere hier irrefuehrend.")
    +
    aktiv = int(np.sum(res.slack < 1e-9))          # wie viele Bedingungen sind straff?
    +if aktiv > len(res.x):
    +    print(f"Entartet: {aktiv} aktive Bedingungen bei {len(res.x)} Variablen.")
    +    print("Einen einzelnen Schattenpreis zu berichten waere hier irrefuehrend.")

    Die belastbare Antwort ist dann keine Zahl, sondern eine Spanne über alle optimalen Dualwerte — Toleranzen_und_Entartung.py rechnet sie vor (Abschnitt 5.9).


    C7 — Widersprüchliche Solver

    @@ -30407,10 +30778,10 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose. -
    print(problem.is_dcp())                      # False?
    -for c in problem.constraints:
    -    print(c.is_dcp(), c)                     # zeigt die Schuldige
    -print(problem.objective.is_dcp())
    +
    print(problem.is_dcp())                      # False?
    +for c in problem.constraints:
    +    print(c.is_dcp(), c)                     # zeigt die Schuldige
    +print(problem.objective.is_dcp())

    C9 — Importfehler

    ImportError: .../highspy/_core...so: undefined symbol: _ZN5Highs13releaseMemoryEv
    @@ -30421,10 +30792,10 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

    Faustregel: Eine Sharpe Ratio über 2 bei einer einfachen Strategie ist fast immer ein Fehler, kein Fund.

    Prüfreihenfolge

    1. Lookahead-Selbsttest.

    -
    signal_vorher = berechne_signal(stichtag)
    -kurse.loc[kurse.index > stichtag] *= 3.0
    -signal_nachher = berechne_signal(stichtag)
    -assert np.allclose(signal_vorher, signal_nachher), "LOOKAHEAD-BIAS!"
    +
    signal_vorher = berechne_signal(stichtag)
    +kurse.loc[kurse.index > stichtag] *= 3.0
    +signal_nachher = berechne_signal(stichtag)
    +assert np.allclose(signal_vorher, signal_nachher), "LOOKAHEAD-BIAS!"

    2. Zeitindizes prüfen. iloc[t] gegen iloc[t+1] — die Gewichte von heute gehören auf die Rendite von morgen.

    3. Survivorship. Wurde das Universum nach Kriterien gefiltert, die erst am Ende bekannt waren (dropna() über den gesamten Zeitraum!)?

    4. Kosten. Sind Gebühren, Spread und Slippage verbucht?

    @@ -30434,15 +30805,15 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

    C11 — Vertauschte Spalten

    Symptom. Ergebnisse sind plausibel, aber falsch beschriftet — oder eine Sektorgrenze greift auf die falschen Titel.

    Ursache. yfinance und viele andere Datenquellen liefern Spalten alphabetisch sortiert, nicht in der Reihenfolge Ihrer Anfrage.

    -
    # IMMER nach dem Import:
    -kurse = roh["Close"][tickers].dropna()          # erzwingt die Reihenfolge
    -assert list(kurse.columns) == tickers, (
    -    f"Reihenfolge weicht ab!\n erwartet: {tickers}\n erhalten: {list(kurse.columns)}")
    -
    -# Und Gruppen NIE ueber Positionsindizes definieren:
    -SEKTOREN = {"Technologie": ["AAPL", "MSFT", "NVDA", "AMZN"]}       # gut
    -tech_idx = [tickers.index(t) for t in SEKTOREN["Technologie"]]     # bricht bei Tippfehler
    -tech_indices = [0, 1, 2, 3]                                        # SCHLECHT
    +
    # IMMER nach dem Import:
    +kurse = roh["Close"][tickers].dropna()          # erzwingt die Reihenfolge
    +assert list(kurse.columns) == tickers, (
    +    f"Reihenfolge weicht ab!\n erwartet: {tickers}\n erhalten: {list(kurse.columns)}")
    +
    +# Und Gruppen NIE ueber Positionsindizes definieren:
    +SEKTOREN = {"Technologie": ["AAPL", "MSFT", "NVDA", "AMZN"]}       # gut
    +tech_idx = [tickers.index(t) for t in SEKTOREN["Technologie"]]     # bricht bei Tippfehler
    +tech_indices = [0, 1, 2, 3]                                        # SCHLECHT

    Die goldene Diagnoseregel

    @@ -30463,51 +30834,51 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

    D1 — SciPy: linprog und milp

    Wofür. Die Einstiegsschicht: keine zusätzliche Installation, HiGHS als Unterbau, ideal für lineare und gemischt-ganzzahlige Probleme in Matrixform. Wofür nicht: alles, was sich nicht als Matrix schreiben lässt, und jede nichtlineare Zielfunktion.

    Lineares Programm

    -
    import numpy as np
    -from scipy.optimize import linprog
    -
    -# linprog MINIMIERT immer -> zum Maximieren die Zielfunktion negieren
    -ergebnis = linprog(
    -    c=[-10.0, -15.0, -25.0],                       # Zielkoeffizienten
    -    A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],    # A_ub @ x <= b_ub
    -    A_eq=None, b_eq=None,                          # Gleichungen, falls vorhanden
    -    bounds=[(0, None)] * 3,                        # je Variable (unten, oben)
    -    method="highs")
    -
    -if ergebnis.status == 0:
    -    print(f"optimal: {-ergebnis.fun:.2f}")         # Vorzeichen zuruecknehmen!
    -    print(f"x = {np.round(ergebnis.x, 4)}")
    -    print(f"Schattenpreise: {-ergebnis.ineqlin.marginals}")
    -elif ergebnis.status == 2:
    -    print("INFEASIBLE - kein zulaessiger Punkt")
    -elif ergebnis.status == 3:
    -    print("UNBOUNDED - Zielfunktion unbeschraenkt")
    -elif ergebnis.status == 1:
    -    print("Iterations- oder Zeitlimit erreicht")
    -else:
    -    print(f"numerisches Problem (status {ergebnis.status}): {ergebnis.message}")
    +
    import numpy as np
    +from scipy.optimize import linprog
    +
    +# linprog MINIMIERT immer -> zum Maximieren die Zielfunktion negieren
    +ergebnis = linprog(
    +    c=[-10.0, -15.0, -25.0],                       # Zielkoeffizienten
    +    A_ub=[[1, 1, 2], [2, 3, 1]], b_ub=[40, 50],    # A_ub @ x <= b_ub
    +    A_eq=None, b_eq=None,                          # Gleichungen, falls vorhanden
    +    bounds=[(0, None)] * 3,                        # je Variable (unten, oben)
    +    method="highs")
    +
    +if ergebnis.status == 0:
    +    print(f"optimal: {-ergebnis.fun:.2f}")         # Vorzeichen zuruecknehmen!
    +    print(f"x = {np.round(ergebnis.x, 4)}")
    +    print(f"Schattenpreise: {-ergebnis.ineqlin.marginals}")
    +elif ergebnis.status == 2:
    +    print("INFEASIBLE - kein zulaessiger Punkt")
    +elif ergebnis.status == 3:
    +    print("UNBOUNDED - Zielfunktion unbeschraenkt")
    +elif ergebnis.status == 1:
    +    print("Iterations- oder Zeitlimit erreicht")
    +else:
    +    print(f"numerisches Problem (status {ergebnis.status}): {ergebnis.message}")

    Ganzzahlig: milp

    -
    import numpy as np
    -from scipy.optimize import milp, LinearConstraint, Bounds
    -
    -# Ganzzahlig: dasselbe Problem, aber x muss ganzzahlig sein
    -ergebnis = milp(
    -    c=[-10.0, -15.0, -25.0],                       # auch milp MINIMIERT
    -    constraints=LinearConstraint([[1, 1, 2], [2, 3, 1]], -np.inf, [40, 50]),
    -    integrality=[1, 1, 1],                         # 0 = stetig, 1 = ganzzahlig
    -    bounds=Bounds(0, np.inf))
    -
    -if ergebnis.status == 0:
    -    print(f"optimal: {-ergebnis.fun:.2f}  x = {np.round(ergebnis.x).astype(int)}")
    -    print(f"MIP-Gap: {ergebnis.mip_gap:.4f}")
    -elif ergebnis.status == 1:
    -    print("Zeitlimit - beste gefundene Loesung nutzen, Gap pruefen")
    -elif ergebnis.status == 2:
    -    print("INFEASIBLE")
    -elif ergebnis.status == 3:
    -    print("UNBOUNDED")
    -else:
    -    print(f"kein Ergebnis: {ergebnis.message}")
    +
    import numpy as np
    +from scipy.optimize import milp, LinearConstraint, Bounds
    +
    +# Ganzzahlig: dasselbe Problem, aber x muss ganzzahlig sein
    +ergebnis = milp(
    +    c=[-10.0, -15.0, -25.0],                       # auch milp MINIMIERT
    +    constraints=LinearConstraint([[1, 1, 2], [2, 3, 1]], -np.inf, [40, 50]),
    +    integrality=[1, 1, 1],                         # 0 = stetig, 1 = ganzzahlig
    +    bounds=Bounds(0, np.inf))
    +
    +if ergebnis.status == 0:
    +    print(f"optimal: {-ergebnis.fun:.2f}  x = {np.round(ergebnis.x).astype(int)}")
    +    print(f"MIP-Gap: {ergebnis.mip_gap:.4f}")
    +elif ergebnis.status == 1:
    +    print("Zeitlimit - beste gefundene Loesung nutzen, Gap pruefen")
    +elif ergebnis.status == 2:
    +    print("INFEASIBLE")
    +elif ergebnis.status == 3:
    +    print("UNBOUNDED")
    +else:
    +    print(f"kein Ergebnis: {ergebnis.message}")

    Die drei häufigsten Stolpersteine

    1. linprog minimiert immer. Zum Maximieren c negieren — und beim Ausgeben des Zielwerts das Vorzeichen wieder zurücknehmen. Dieselbe Negation dreht auch die Schattenpreise (Abschnitt 5.7).
    2. @@ -30517,41 +30888,41 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

      D2 — HiGHS über highspy

      Wofür. Derselbe Solver wie unter SciPy, aber direkt gesteuert: Optionen, Warm-Starts, inkrementelles Ändern eines bestehenden Modells. Wofür nicht: schnelles Hinschreiben — die CSR-Matrixübergabe ist fehleranfällig.

      -
      import numpy as np
      -import highspy
      -
      -h = highspy.Highs()
      -h.setOptionValue("output_flag", False)             # Solverprotokoll abschalten
      -h.setOptionValue("time_limit", 60.0)
      -h.setOptionValue("mip_rel_gap", 0.01)              # 1 % Gap genuegt
      -
      -# Variablen: Anzahl, Untergrenzen, Obergrenzen
      -h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
      -h.changeObjectiveSense(highspy.ObjSense.kMaximize)
      -for spalte, wert in enumerate([10.0, 15.0, 25.0]):
      -    h.changeColCost(spalte, wert)
      -
      -# Zeilen im CSR-Format: starts[i] = Beginn von Zeile i in indices/values
      -h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
      -          np.array([0, 3], dtype=np.int32),                  # starts
      -          np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),      # Spaltenindizes
      -          np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))          # Koeffizienten
      -# Ganzzahligkeit: h.changeColsIntegrality(...) mit highspy.HighsVarType.kInteger
      -
      -h.run()
      -status = h.getModelStatus()
      -if status == highspy.HighsModelStatus.kOptimal:
      -    print(f"optimal: {h.getInfo().objective_function_value:.2f}")
      -    print(f"x = {np.round(h.getSolution().col_value[:3], 4)}")
      -    print(f"Schattenpreise: {np.round(h.getSolution().row_dual[:2], 4)}")
      -elif status == highspy.HighsModelStatus.kInfeasible:
      -    print("INFEASIBLE")
      -elif status == highspy.HighsModelStatus.kUnbounded:
      -    print("UNBOUNDED")
      -elif status == highspy.HighsModelStatus.kTimeLimit:
      -    print(f"Zeitlimit, Gap {h.getInfo().mip_gap:.3f}")
      -else:
      -    print("kein Optimum:", h.modelStatusToString(status))
      +
      import numpy as np
      +import highspy
      +
      +h = highspy.Highs()
      +h.setOptionValue("output_flag", False)             # Solverprotokoll abschalten
      +h.setOptionValue("time_limit", 60.0)
      +h.setOptionValue("mip_rel_gap", 0.01)              # 1 % Gap genuegt
      +
      +# Variablen: Anzahl, Untergrenzen, Obergrenzen
      +h.addVars(3, np.zeros(3), np.full(3, highspy.kHighsInf))
      +h.changeObjectiveSense(highspy.ObjSense.kMaximize)
      +for spalte, wert in enumerate([10.0, 15.0, 25.0]):
      +    h.changeColCost(spalte, wert)
      +
      +# Zeilen im CSR-Format: starts[i] = Beginn von Zeile i in indices/values
      +h.addRows(2, np.full(2, -highspy.kHighsInf), np.array([40.0, 50.0]), 6,
      +          np.array([0, 3], dtype=np.int32),                  # starts
      +          np.array([0, 1, 2, 0, 1, 2], dtype=np.int32),      # Spaltenindizes
      +          np.array([1.0, 1.0, 2.0, 2.0, 3.0, 1.0]))          # Koeffizienten
      +# Ganzzahligkeit: h.changeColsIntegrality(...) mit highspy.HighsVarType.kInteger
      +
      +h.run()
      +status = h.getModelStatus()
      +if status == highspy.HighsModelStatus.kOptimal:
      +    print(f"optimal: {h.getInfo().objective_function_value:.2f}")
      +    print(f"x = {np.round(h.getSolution().col_value[:3], 4)}")
      +    print(f"Schattenpreise: {np.round(h.getSolution().row_dual[:2], 4)}")
      +elif status == highspy.HighsModelStatus.kInfeasible:
      +    print("INFEASIBLE")
      +elif status == highspy.HighsModelStatus.kUnbounded:
      +    print("UNBOUNDED")
      +elif status == highspy.HighsModelStatus.kTimeLimit:
      +    print(f"Zeitlimit, Gap {h.getInfo().mip_gap:.3f}")
      +else:
      +    print("kein Optimum:", h.modelStatusToString(status))

      Die drei häufigsten Stolpersteine

      1. Nicht zusammen mit ortools importieren. Beide bringen eine eigene HiGHS-Kopie mit; im selben Prozess endet das in undefined symbol (Anhang C, C9). Auch cvxpy zieht highspy bei der Solver-Erkennung mit hinein.
      2. @@ -30561,34 +30932,34 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

        D3 — OR-Tools: pywraplp

        Wofür. Bequeme algebraische Schreibweise für LP und MILP mit umschaltbarem Backend (GLOP, SCIP, CBC, SAT). Wofür nicht: Scheduling und kombinatorische Bedingungen — dafür ist CP-SAT (D4) da.

        -
        from ortools.linear_solver import pywraplp
        -
        -# "GLOP" = LP, "SCIP" oder "CBC" = MILP, "SAT" = CP-SAT als MILP-Backend
        -loeser = pywraplp.Solver.CreateSolver("GLOP")
        -if loeser is None:
        -    raise SystemExit("Solver nicht verfuegbar")
        -loeser.SetTimeLimit(60_000)                        # Millisekunden!
        -
        -unendlich = loeser.infinity()
        -x = [loeser.NumVar(0, unendlich, f"x{j}") for j in range(3)]
        -# ganzzahlig: loeser.IntVar(0, unendlich, "n") | binaer: loeser.BoolVar("b")
        -
        -loeser.Add(x[0] + x[1] + 2 * x[2] <= 40)
        -loeser.Add(2 * x[0] + 3 * x[1] + x[2] <= 50)
        -loeser.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
        -
        -status = loeser.Solve()
        -if status == pywraplp.Solver.OPTIMAL:
        -    print(f"optimal: {loeser.Objective().Value():.2f}")
        -    print(f"x = {[round(v.solution_value(), 4) for v in x]}")
        -elif status == pywraplp.Solver.FEASIBLE:
        -    print("zulaessig, nicht bewiesen optimal (Zeitlimit)")
        -elif status == pywraplp.Solver.INFEASIBLE:
        -    print("INFEASIBLE")
        -elif status == pywraplp.Solver.UNBOUNDED:
        -    print("UNBOUNDED")
        -else:
        -    print("ABNORMAL / NOT_SOLVED - Modell oder Solver pruefen")
        +
        from ortools.linear_solver import pywraplp
        +
        +# "GLOP" = LP, "SCIP" oder "CBC" = MILP, "SAT" = CP-SAT als MILP-Backend
        +loeser = pywraplp.Solver.CreateSolver("GLOP")
        +if loeser is None:
        +    raise SystemExit("Solver nicht verfuegbar")
        +loeser.SetTimeLimit(60_000)                        # Millisekunden!
        +
        +unendlich = loeser.infinity()
        +x = [loeser.NumVar(0, unendlich, f"x{j}") for j in range(3)]
        +# ganzzahlig: loeser.IntVar(0, unendlich, "n") | binaer: loeser.BoolVar("b")
        +
        +loeser.Add(x[0] + x[1] + 2 * x[2] <= 40)
        +loeser.Add(2 * x[0] + 3 * x[1] + x[2] <= 50)
        +loeser.Maximize(10 * x[0] + 15 * x[1] + 25 * x[2])
        +
        +status = loeser.Solve()
        +if status == pywraplp.Solver.OPTIMAL:
        +    print(f"optimal: {loeser.Objective().Value():.2f}")
        +    print(f"x = {[round(v.solution_value(), 4) for v in x]}")
        +elif status == pywraplp.Solver.FEASIBLE:
        +    print("zulaessig, nicht bewiesen optimal (Zeitlimit)")
        +elif status == pywraplp.Solver.INFEASIBLE:
        +    print("INFEASIBLE")
        +elif status == pywraplp.Solver.UNBOUNDED:
        +    print("UNBOUNDED")
        +else:
        +    print("ABNORMAL / NOT_SOLVED - Modell oder Solver pruefen")

        Die drei häufigsten Stolpersteine

        1. CreateSolver liefert None, wenn der Backend-Name unbekannt oder nicht gebaut ist — immer prüfen, statt am None später zu scheitern.
        2. @@ -30598,40 +30969,40 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

          D4 — CP-SAT: cp_model

          Wofür. Scheduling, Zuordnung, Reihenfolgen, alles Kombinatorische mit globalen Bedingungen. Wofür nicht: stetige Größen — CP-SAT rechnet ausschließlich ganzzahlig. Wer Nachkommastellen braucht, skaliert (Cent statt Euro, Promille statt Anteil).

          -
          from ortools.sat.python import cp_model
          -
          -modell = cp_model.CpModel()
          -
          -# Variablen - CP-SAT rechnet ausschliesslich mit GANZEN Zahlen
          -x = modell.NewIntVar(0, 100, "x")                  # untere, obere Schranke, Name
          -y = modell.NewIntVar(0, 100, "y")
          -b = modell.NewBoolVar("b")                         # 0/1
          -
          -# Nebenbedingungen
          -modell.Add(2 * x + 3 * y <= 50)
          -modell.Add(x >= 5).OnlyEnforceIf(b)                # gilt nur, wenn b wahr ist
          -modell.AddAllDifferent([x, y])                     # globale Bedingung
          -modell.AddMaxEquality(z := modell.NewIntVar(0, 100, "z"), [x, y])
          -
          -modell.Maximize(10 * x + 15 * y - 3 * z)
          -
          -loeser = cp_model.CpSolver()
          -loeser.parameters.max_time_in_seconds = 10.0
          -loeser.parameters.num_workers = 1                  # 1 = reproduzierbar
          -loeser.parameters.random_seed = 1
          -status = loeser.Solve(modell)
          -
          -if status == cp_model.OPTIMAL:
          -    print(f"optimal: {loeser.ObjectiveValue():.0f}  x={loeser.Value(x)} y={loeser.Value(y)}")
          -elif status == cp_model.FEASIBLE:
          -    print(f"zulaessig, nicht bewiesen optimal - Gap-Schranke: {loeser.BestObjectiveBound()}")
          -elif status == cp_model.INFEASIBLE:
          -    print("INFEASIBLE - Bedingungen widersprechen sich")
          -elif status == cp_model.MODEL_INVALID:
          -    print("Modellfehler:", modell.Validate())
          -else:
          -    print("UNKNOWN - Zeit abgelaufen, ohne eine Loesung zu finden")
          -print(f"Laufzeit {loeser.WallTime():.3f}s, {loeser.NumBranches()} Verzweigungen")
          +
          from ortools.sat.python import cp_model
          +
          +modell = cp_model.CpModel()
          +
          +# Variablen - CP-SAT rechnet ausschliesslich mit GANZEN Zahlen
          +x = modell.NewIntVar(0, 100, "x")                  # untere, obere Schranke, Name
          +y = modell.NewIntVar(0, 100, "y")
          +b = modell.NewBoolVar("b")                         # 0/1
          +
          +# Nebenbedingungen
          +modell.Add(2 * x + 3 * y <= 50)
          +modell.Add(x >= 5).OnlyEnforceIf(b)                # gilt nur, wenn b wahr ist
          +modell.AddAllDifferent([x, y])                     # globale Bedingung
          +modell.AddMaxEquality(z := modell.NewIntVar(0, 100, "z"), [x, y])
          +
          +modell.Maximize(10 * x + 15 * y - 3 * z)
          +
          +loeser = cp_model.CpSolver()
          +loeser.parameters.max_time_in_seconds = 10.0
          +loeser.parameters.num_workers = 1                  # 1 = reproduzierbar
          +loeser.parameters.random_seed = 1
          +status = loeser.Solve(modell)
          +
          +if status == cp_model.OPTIMAL:
          +    print(f"optimal: {loeser.ObjectiveValue():.0f}  x={loeser.Value(x)} y={loeser.Value(y)}")
          +elif status == cp_model.FEASIBLE:
          +    print(f"zulaessig, nicht bewiesen optimal - Gap-Schranke: {loeser.BestObjectiveBound()}")
          +elif status == cp_model.INFEASIBLE:
          +    print("INFEASIBLE - Bedingungen widersprechen sich")
          +elif status == cp_model.MODEL_INVALID:
          +    print("Modellfehler:", modell.Validate())
          +else:
          +    print("UNKNOWN - Zeit abgelaufen, ohne eine Loesung zu finden")
          +print(f"Laufzeit {loeser.WallTime():.3f}s, {loeser.NumBranches()} Verzweigungen")

          Die drei häufigsten Stolpersteine

          1. Alles ist ganzzahlig. 0.5 * x gibt es nicht. Skalieren Sie das ganze Modell auf eine feinere Einheit, statt zu runden.
          2. @@ -30641,32 +31012,32 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

            D5 — CVXPY

            Wofür. Konvexe Probleme: quadratische Ziele, Normen, CVaR, alles mit Regularisierungstermen. Wofür nicht: große kombinatorische Modelle — der Aufbau der Ausdrücke wird dann selbst zum Engpass.

            -
            import numpy as np
            -import cvxpy as cp
            -
            -x = cp.Variable(3, nonneg=True)                    # nonneg=True statt x >= 0
            -gewichte = cp.Variable(3)
            -ganzzahlig = cp.Variable(3, integer=True)          # macht daraus ein MIP
            -
            -ziel = cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x)
            -bedingungen = [np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40.0, 50.0])]
            -
            -problem = cp.Problem(ziel, bedingungen)
            -if not problem.is_dcp():                           # VOR dem Loesen pruefen
            -    print("nicht DCP:", [c for c in bedingungen if not c.is_dcp()])
            -problem.solve()
            -
            -if problem.status == cp.OPTIMAL:
            -    print(f"optimal: {problem.value:.2f}  x = {np.round(x.value, 4)}")
            -    print(f"Schattenpreis: {np.round(bedingungen[0].dual_value, 4)}")
            -elif problem.status == cp.OPTIMAL_INACCURATE:
            -    print("Loesung numerisch unsicher - Skalierung pruefen, anderen Solver testen")
            -elif problem.status == cp.INFEASIBLE:
            -    print("INFEASIBLE")
            -elif problem.status == cp.UNBOUNDED:
            -    print("UNBOUNDED")
            -else:
            -    print("Solverfehler:", problem.status)
            +
            import numpy as np
            +import cvxpy as cp
            +
            +x = cp.Variable(3, nonneg=True)                    # nonneg=True statt x >= 0
            +gewichte = cp.Variable(3)
            +ganzzahlig = cp.Variable(3, integer=True)          # macht daraus ein MIP
            +
            +ziel = cp.Maximize(np.array([10.0, 15.0, 25.0]) @ x)
            +bedingungen = [np.array([[1, 1, 2], [2, 3, 1]]) @ x <= np.array([40.0, 50.0])]
            +
            +problem = cp.Problem(ziel, bedingungen)
            +if not problem.is_dcp():                           # VOR dem Loesen pruefen
            +    print("nicht DCP:", [c for c in bedingungen if not c.is_dcp()])
            +problem.solve()
            +
            +if problem.status == cp.OPTIMAL:
            +    print(f"optimal: {problem.value:.2f}  x = {np.round(x.value, 4)}")
            +    print(f"Schattenpreis: {np.round(bedingungen[0].dual_value, 4)}")
            +elif problem.status == cp.OPTIMAL_INACCURATE:
            +    print("Loesung numerisch unsicher - Skalierung pruefen, anderen Solver testen")
            +elif problem.status == cp.INFEASIBLE:
            +    print("INFEASIBLE")
            +elif problem.status == cp.UNBOUNDED:
            +    print("UNBOUNDED")
            +else:
            +    print("Solverfehler:", problem.status)

            Die drei häufigsten Stolpersteine

            1. DCP-Regeln vor dem Lösen prüfen. problem.is_dcp() und dann die einzelnen Bedingungen — das nennt die Schuldige, statt einen DCPError ohne Ort zu werfen (Anhang C, C8).
            2. @@ -30677,73 +31048,73 @@ Insgesamt 2874 Solveraufrufe fuer die gesamte Diagnose.

              D6 — Modellierungssprachen: Pyomo und Linopy

              Wofür. Beide trennen Modell von Solver: dasselbe Modell läuft ohne Änderung unter HiGHS, CBC, Gurobi. Pyomo denkt in Mengen und Indizes wie eine mathematische Formulierung; Linopy denkt in beschrifteten Arrays und baut Nebenbedingungen als Matrixoperation statt in Python-Schleifen. Wofür nicht: ein Modell mit zehn Nebenbedingungen — dort ist der Aufwand höher als der Nutzen.

              Pyomo

              -
              import pyomo.environ as pyo
              -
              -modell = pyo.ConcreteModel()
              -modell.J = pyo.RangeSet(0, 2)                      # Indexmenge
              -modell.x = pyo.Var(modell.J, domain=pyo.NonNegativeReals)
              -# ganzzahlig: domain=pyo.NonNegativeIntegers | binaer: domain=pyo.Binary
              -
              -ertrag = {0: 10.0, 1: 15.0, 2: 25.0}
              -modell.ziel = pyo.Objective(expr=sum(ertrag[j] * modell.x[j] for j in modell.J),
              -                            sense=pyo.maximize)
              -modell.montage = pyo.Constraint(
              -    expr=modell.x[0] + modell.x[1] + 2 * modell.x[2] <= 40)
              -modell.pruefung = pyo.Constraint(
              -    expr=2 * modell.x[0] + 3 * modell.x[1] + modell.x[2] <= 50)
              -modell.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)   # fuer Schattenpreise
              -
              -ergebnis = pyo.SolverFactory("appsi_highs").solve(modell)
              -zustand = ergebnis.solver.termination_condition
              -if zustand == pyo.TerminationCondition.optimal:
              -    print(f"optimal: {pyo.value(modell.ziel):.2f}")
              -    print(f"x = {[round(pyo.value(modell.x[j]), 4) for j in modell.J]}")
              -    print(f"Schattenpreis Montage: {modell.dual[modell.montage]:.4f}")
              -elif zustand == pyo.TerminationCondition.infeasible:
              -    print("INFEASIBLE")
              -elif zustand == pyo.TerminationCondition.unbounded:
              -    print("UNBOUNDED")
              -elif zustand == pyo.TerminationCondition.maxTimeLimit:
              -    print("Zeitlimit")
              -else:
              -    print("kein Optimum:", zustand)
              +
              import pyomo.environ as pyo
              +
              +modell = pyo.ConcreteModel()
              +modell.J = pyo.RangeSet(0, 2)                      # Indexmenge
              +modell.x = pyo.Var(modell.J, domain=pyo.NonNegativeReals)
              +# ganzzahlig: domain=pyo.NonNegativeIntegers | binaer: domain=pyo.Binary
              +
              +ertrag = {0: 10.0, 1: 15.0, 2: 25.0}
              +modell.ziel = pyo.Objective(expr=sum(ertrag[j] * modell.x[j] for j in modell.J),
              +                            sense=pyo.maximize)
              +modell.montage = pyo.Constraint(
              +    expr=modell.x[0] + modell.x[1] + 2 * modell.x[2] <= 40)
              +modell.pruefung = pyo.Constraint(
              +    expr=2 * modell.x[0] + 3 * modell.x[1] + modell.x[2] <= 50)
              +modell.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)   # fuer Schattenpreise
              +
              +ergebnis = pyo.SolverFactory("appsi_highs").solve(modell)
              +zustand = ergebnis.solver.termination_condition
              +if zustand == pyo.TerminationCondition.optimal:
              +    print(f"optimal: {pyo.value(modell.ziel):.2f}")
              +    print(f"x = {[round(pyo.value(modell.x[j]), 4) for j in modell.J]}")
              +    print(f"Schattenpreis Montage: {modell.dual[modell.montage]:.4f}")
              +elif zustand == pyo.TerminationCondition.infeasible:
              +    print("INFEASIBLE")
              +elif zustand == pyo.TerminationCondition.unbounded:
              +    print("UNBOUNDED")
              +elif zustand == pyo.TerminationCondition.maxTimeLimit:
              +    print("Zeitlimit")
              +else:
              +    print("kein Optimum:", zustand)

              Linopy

              -
              import linopy
              -import numpy as np
              -import pandas as pd
              -import xarray as xr
              -
              -# Benannte Indizes statt blosser Listen - sonst heissen die Achsen "dim_0"
              -produkt = pd.Index(["Rahmen", "Gehaeuse", "Deckel"], name="produkt")
              -ressource = pd.Index(["Montage", "Pruefung"], name="ressource")
              -
              -modell = linopy.Model()
              -modell.add_variables(lower=0, coords=[produkt], name="menge")
              -x = modell.variables["menge"]
              -# ganzzahlig: integer=True | binaer: binary=True
              -
              -ertrag = xr.DataArray([10.0, 15.0, 25.0], coords=[produkt])
              -verbrauch = xr.DataArray([[1.0, 1.0, 2.0], [2.0, 3.0, 1.0]],
              -                         coords=[ressource, produkt])
              -vorrat = xr.DataArray([40.0, 50.0], coords=[ressource])
              -
              -modell.add_objective((ertrag * x).sum(), sense="max")
              -# EINE Zeile, so viele Nebenbedingungen wie 'ressource' Eintraege hat:
              -modell.add_constraints((verbrauch * x).sum("produkt") <= vorrat, name="kapazitaet")
              -
              -modell.solve(solver_name="highs", output_flag=False)
              -
              -if modell.termination_condition == "optimal":
              -    print(f"optimal: {modell.objective.value:.2f}")
              -    print(x.solution.to_series().round(4).to_dict())
              -    print("Schattenpreise:",
              -          modell.constraints["kapazitaet"].dual.to_series().round(4).to_dict())
              -elif modell.termination_condition == "infeasible":
              -    print("INFEASIBLE")
              -elif modell.termination_condition == "unbounded":
              -    print("UNBOUNDED")
              -else:
              -    print("kein Optimum:", modell.status, modell.termination_condition)
              +
              import linopy
              +import numpy as np
              +import pandas as pd
              +import xarray as xr
              +
              +# Benannte Indizes statt blosser Listen - sonst heissen die Achsen "dim_0"
              +produkt = pd.Index(["Rahmen", "Gehaeuse", "Deckel"], name="produkt")
              +ressource = pd.Index(["Montage", "Pruefung"], name="ressource")
              +
              +modell = linopy.Model()
              +modell.add_variables(lower=0, coords=[produkt], name="menge")
              +x = modell.variables["menge"]
              +# ganzzahlig: integer=True | binaer: binary=True
              +
              +ertrag = xr.DataArray([10.0, 15.0, 25.0], coords=[produkt])
              +verbrauch = xr.DataArray([[1.0, 1.0, 2.0], [2.0, 3.0, 1.0]],
              +                         coords=[ressource, produkt])
              +vorrat = xr.DataArray([40.0, 50.0], coords=[ressource])
              +
              +modell.add_objective((ertrag * x).sum(), sense="max")
              +# EINE Zeile, so viele Nebenbedingungen wie 'ressource' Eintraege hat:
              +modell.add_constraints((verbrauch * x).sum("produkt") <= vorrat, name="kapazitaet")
              +
              +modell.solve(solver_name="highs", output_flag=False)
              +
              +if modell.termination_condition == "optimal":
              +    print(f"optimal: {modell.objective.value:.2f}")
              +    print(x.solution.to_series().round(4).to_dict())
              +    print("Schattenpreise:",
              +          modell.constraints["kapazitaet"].dual.to_series().round(4).to_dict())
              +elif modell.termination_condition == "infeasible":
              +    print("INFEASIBLE")
              +elif modell.termination_condition == "unbounded":
              +    print("UNBOUNDED")
              +else:
              +    print("kein Optimum:", modell.status, modell.termination_condition)

              Die drei häufigsten Stolpersteine

              1. Der Solver ist ein eigenes Programm. SolverFactory("appsi_highs") scheitert, wenn HiGHS nicht auffindbar ist — die Fehlermeldung nennt dann das Modell, nicht die fehlende Installation.
              2. diff --git a/OR_HTML_04/index.html b/OR_HTML_04/index.html index 6b188ad..64a753c 100644 --- a/OR_HTML_04/index.html +++ b/OR_HTML_04/index.html @@ -128,7 +128,7 @@

                Lernpfade

                Pfad A — Vollständiger Lehrgang (100–140 Stunden)

                Kapitel 1 bis Kapitel 23 in Reihenfolge, danach ein Projekt aus der Projektwerkstatt. Empfohlen, wenn Sie OR systematisch lernen wollen. Rechnen Sie mit 4–6 Stunden je Kapitel inklusive Übungen.

                Pfad B — Planung, Disposition, Personaleinsatz (ca. 25 Stunden)

                Kapitel 1 (Bausteine) → Kapitel 2 (bis Abschnitt 2.5) → Kapitel 4 (vom Wunsch zum Modell) → Kapitel 5 (LP, Schattenpreise) → Kapitel 6 (Ja/Nein-Entscheidungen) → Kapitel 7 (CP-SAT, Scheduling) → Projekt P1 (Vertretungsplaner) oder P2 (Schichtplanung). Das ist der kürzeste Weg zu einem einsetzbaren Dienstplan-Optimierer.

                Pfad C — Logistik und Tourenplanung (ca. 25 Stunden)

                Kapitel 1Kapitel 2 (bis Abschnitt 2.4) → Kapitel 5Kapitel 6Kapitel 8 (Graphen, VRP) → Projekt P3 (Liefertouren) oder P4 (Lagernetzwerk).

                Pfad D — Quantitative Finance (ca. 30 Stunden)

                Kapitel 1Kapitel 2Kapitel 11 (QP, KKT) → Kapitel 18 (Daten, Shrinkage) → Kapitel 19 (Markowitz) → Kapitel 20 (CVaR) → Kapitel 21 (Backtest) → Projekt P6 (Portfolio-Rebalancer) oder P7 (Risikoreport). Wichtig: Überspringen Sie Kapitel 18 nicht. Wer direkt bei Markowitz einsteigt, optimiert Schätzrauschen und wundert sich über absurde Gewichte.

                Pfad E — Ich habe morgen ein konkretes Problem (2–4 Stunden)

                Lesen Sie Abschnitt 1.5 (die vier Bausteine), dann Anhang B ([Modellierungsmuster](anhang-modellierungsmuster.html)) und suchen Sie dort das Muster, das zu Ihrem Problem passt. Von jedem Muster führt ein Verweis in das zuständige Kapitel. Wie die gewählte Bibliothek es schreibt, steht in Anhang D ([Spickzettel](anhang-spickzettel.html)); wenn etwas nicht läuft, in Anhang C ([Fehlerdiagnose](anhang-fehlerdiagnose.html)).

                -

                Teil I: Grundlagen des Operations Research

                Teil II: Die Kernverfahren der deterministischen Optimierung

                Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik

                Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte

                Teil V: Praxis

                +

                Teil I: Grundlagen des Operations Research

                Teil II: Die Kernverfahren der deterministischen Optimierung

                Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik

                Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte

                Teil V: Praxis

                diff --git a/OR_HTML_04/programme.html b/OR_HTML_04/programme.html index d50b458..ce6247d 100644 --- a/OR_HTML_04/programme.html +++ b/OR_HTML_04/programme.html @@ -99,7 +99,7 @@
                -

                Beispielprogramme

                Alle 73 im Buch abgedruckten Programme, gruppiert nach Kapitel, zum direkten Herunterladen.

                Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung

                Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität

                Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten

                Kapitel 4: Vom Management-Wunsch zum Modell

                Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise

                Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound

                Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung

                Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP

                Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt

                Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten

                Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität

                Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit

                Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution

                Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte

                Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung

                Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten

                Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit

                Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage

                Kapitel 19: Die moderne Portfoliotheorie nach Markowitz

                Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten

                Kapitel 21: Die vollständige quantitative Handelsmaschine

                Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz

                Kapitel 23: Testen, Messen, Ausliefern

                +

                Beispielprogramme

                Alle 74 im Buch abgedruckten Programme, gruppiert nach Kapitel, zum direkten Herunterladen.

                Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung

                Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität

                Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten

                Kapitel 4: Vom Management-Wunsch zum Modell

                Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise

                Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound

                Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung

                Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP

                Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt

                Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten

                Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität

                Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit

                Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution

                Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte

                Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung

                Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten

                Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit

                Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage

                Kapitel 19: Die moderne Portfoliotheorie nach Markowitz

                Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten

                Kapitel 21: Die vollständige quantitative Handelsmaschine

                Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz

                Kapitel 23: Testen, Messen, Ausliefern

                diff --git a/OR_HTML_04/programme/Chance_Constraints.py b/OR_HTML_04/programme/Chance_Constraints.py new file mode 100644 index 0000000..6cd90c1 --- /dev/null +++ b/OR_HTML_04/programme/Chance_Constraints.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 + +# Chance_Constraints.py +""" +Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen. + +Das Management fragt selten "was ist im Mittel am besten?", sondern "mit +welcher Sicherheit haelt der Plan?". Genau das formuliert eine Chance +Constraint: P(Versorgung >= Bedarf) >= 1 - alpha. + +Gezeigt werden beide Wege dorthin, an derselben Instanz: + (a) analytisch - unter Normalverteilungsannahme wird daraus eine + Second-Order-Cone-Bedingung, loesbar mit CVXPY + (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige + empirische Verteilungen + +Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt +Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung +nicht normal ist? + +Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools +noch ein direkter highspy-Import, damit alles in einem Prozess laeuft. +""" + +import cvxpy as cp +import numpy as np +from scipy.stats import norm + +# --- Der Kraftwerkspark ---------------------------------------------------- +# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel +# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und +# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche, +# Genehmigung, Brennstoffversorgung. +TECHNIK = ["Gaskraftwerk", "Windpark", "Solarpark", "Biomasse"] +KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr +VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85]) +STREUUNG = np.array([0.05, 0.16, 0.10, 0.04]) +GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW + +# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und +# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel +# und nicht zu einer Summe unabhaengiger Einzelzuschlaege. +KORRELATION = np.array([ + [1.00, 0.00, 0.00, 0.05], + [0.00, 1.00, -0.25, 0.00], + [0.00, -0.25, 1.00, 0.00], + [0.05, 0.00, 0.00, 1.00], +]) +SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG) +WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA + +BEDARF = 500.0 # MW, die gesichert bereitstehen muessen +N = len(TECHNIK) + +# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich. +# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das +# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt. +# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken. +P_KAELTEWELLE = 0.08 +EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88]) + + +def mittelwertplan(): + """Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.""" + x = cp.Variable(N, nonneg=True) + problem = cp.Problem(cp.Minimize(KOSTEN @ x), + [VERFUEGBAR @ x >= BEDARF, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_analytisch(alpha): + """ + P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA). + + Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF + mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der + Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage. + Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken + gemeinsam weniger als jede fuer sich. + """ + x = cp.Variable(N, nonneg=True) + z = norm.ppf(1 - alpha) + bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF + problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_szenarien(a, alpha): + """ + Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine + Binaervariable z_s, ob es verletzt werden darf. + + a_s^T x >= BEDARF - M * z_s fuer alle s + sum_s z_s <= alpha * S + + M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0: + Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M + wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die + Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung. + """ + S = len(a) + x = cp.Variable(N, nonneg=True) + z = cp.Variable(S, boolean=True) + problem = cp.Problem( + cp.Minimize(KOSTEN @ x), + [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE]) + problem.solve(solver=cp.SCIPY) + return problem.value, x.value + + +def ziehe_wetter(anzahl, seed): + """Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.""" + rng = np.random.default_rng(seed) + a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl) + getroffen = rng.random(anzahl) < P_KAELTEWELLE + a[getroffen] *= EINBRUCH + return np.clip(a, 0.0, 1.0) + + +def sicherheit(x, a): + """Anteil der Szenarien, in denen der Plan den Bedarf deckt.""" + return float(np.mean(a @ x >= BEDARF)) + + +KOPF = (f"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} " + f"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}") + + +def zeile(name, kosten, x, in_normal, in_echt): + mix = " ".join(f"{w:4.0f}" for w in x) + return (f"{name:<20} {kosten:>13,.0f} {mix} " + f"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %") + + +if __name__ == "__main__": + print("=" * 78) + print(" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK") + print("=" * 78) + print(f"Gesichert bereitzustellen: {BEDARF:.0f} MW\n") + print(f"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} " + f"{'Grenze':>8} {'EUR je erw. MW':>15}") + print("-" * 78) + for i, name in enumerate(TECHNIK): + print(f"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} " + f"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} " + f"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}") + print(f"\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, " + f"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.") + + # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen - + # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung. + echt = ziehe_wetter(200_000, seed=771) + normalwelt = np.random.default_rng(4711).multivariate_normal( + VERFUEGBAR, SIGMA, size=200_000) + + print("\n" + "=" * 78) + print(" (1) Mittelwertplan und Chance Constraints im Vergleich") + print("=" * 78) + print(KOPF) + print("-" * 78) + + k_mittel, x_mittel = mittelwertplan() + stufen = [("Mittelwert", k_mittel, sicherheit(x_mittel, normalwelt))] + print(zeile("Mittelwert", k_mittel, x_mittel, + sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt))) + + plaene = {} + for alpha in (0.20, 0.10, 0.05, 0.01): + kosten, x = chance_constraint_analytisch(alpha) + plaene[alpha] = (kosten, x) + quote_normal = sicherheit(x, normalwelt) + stufen.append((f"Zusage {(1 - alpha) * 100:.0f} %", kosten, quote_normal)) + print(zeile(f"Zusage {(1 - alpha) * 100:.0f} %", kosten, x, + quote_normal, sicherheit(x, echt))) + + print("\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren") + print(" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).") + + print("\n" + "=" * 78) + print(" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?") + print("=" * 78) + print(f"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} " + f"{'EUR je Punkt':>13}") + print("-" * 78) + for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]): + d_punkte = (q2 - q1) * 100 + d_kosten = k2 - k1 + print(f"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} " + f"{d_kosten / d_punkte:>13,.0f}") + erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100) + letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100) + print(f"\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.") + print(" Sicherheit ist konvex bepreist - genau deshalb muss jemand") + print(" entscheiden, wie viel davon das Unternehmen kaufen will.") + + print("\n" + "=" * 78) + print(" (3) Und wenn die Verteilung nicht normal ist?") + print("=" * 78) + k_soc, x_soc = plaene[0.05] + print(f"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in " + f"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern") + print(f"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % " + f"und - entscheidend - auch das Gaskraftwerk nur " + f"{EINBRUCH[0] * 100:.0f} %") + print("des Ueblichen. Alles bricht gleichzeitig ein.\n") + + a_bau = ziehe_wetter(400, seed=20260908) + k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05) + + print(KOPF) + print("-" * 78) + print(zeile("SOC, Zusage 95 %", k_soc, x_soc, + sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt))) + print(zeile("Szenarien, 95 %", k_sz, x_sz, + sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt))) + + print(f"\n Der SOC-Plan verspricht 95 % und haelt " + f"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist") + print(f" falsch, sondern die Annahme: In der Normalwelt liefert derselbe") + print(f" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.") + print(f"\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} " + f"Binaervariablen) kommt auf") + print(f" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer " + f"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:") + print(" Was im Ernstfall ausfaellt, hilft der Zusage nicht.") + print("=" * 78) diff --git a/OR_HTML_04/stichwortverzeichnis.html b/OR_HTML_04/stichwortverzeichnis.html index b6f290e..cd998b8 100644 --- a/OR_HTML_04/stichwortverzeichnis.html +++ b/OR_HTML_04/stichwortverzeichnis.html @@ -99,7 +99,7 @@
                -

                Stichwortverzeichnis

                217 Fachbegriffe. Ein Klick führt zum jeweiligen Kapitel/Abschnitt (statt einer im Web bedeutungslosen Seitenzahl wie im gedruckten Register).

                A

                Absolutbetrag (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Aktivierungsschalter (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                AlternativoptimaAnhang E: Glossar und Literatur

                B

                Bedingte Kopplung (Konjunktion)Anhang B: Katalog der Modellierungsmuster

                C

                D

                DualitätstheorieAnhang E: Glossar und Literatur

                E

                Entweder-Oder-BedingungAnhang B: Katalog der Modellierungsmuster

                F

                Falsche Dualwerte (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                G

                Gestaffelte Preise (stückweise linear)Anhang B: Katalog der Modellierungsmuster
                Gini-KoeffizientProjektwerkstatt

                H

                Herfindahl-IndexProjektwerkstatt

                I

                IIS (Irreducible Infeasible Subset)Anhang C: Fehlerdiagnose-Handbuch
                Implikation (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Importfehler (ortools und highspy)Anhang C: Fehlerdiagnose-Handbuch
                Instabile Lösung (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                J

                K

                Kanonische StandardformAnhang E: Glossar und Literatur
                Kohärentes RisikomaßAnhang E: Glossar und Literatur

                L

                Lagrange-MultiplikatorAnhang E: Glossar und Literatur
                Laufzeitexplosion (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Lineare Programmierung (LP)Anhang E: Glossar und Literatur

                M

                Maximum DrawdownAnhang E: Glossar und Literatur

                N

                Nichtlineare Programmierung (NLP)Anhang E: Glossar und Literatur

                O

                P

                Positiv (semi-)definitAnhang E: Glossar und Literatur

                Q

                R

                S

                T

                TransaktionskostenAnhang E: Glossar und Literatur

                U

                Umrüstkosten (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Unbounded (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                UnsicherheitsmengeAnhang E: Glossar und Literatur
                Unsinniges Ergebnis (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                V

                Vehicle Routing Problem (VRP)Anhang E: Glossar und Literatur
                Verdächtig guter Backtest (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Vertauschte Spalten (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                W

                Weiche Grenze mit StrafkostenAnhang B: Katalog der Modellierungsmuster
                Widersprüchliche Solver (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Wurzel-Zeit-RegelAnhang E: Glossar und Literatur

                Z

                Zuordnungsproblem (1:1)Anhang B: Katalog der Modellierungsmuster
                Zwei-Phasen-Simplex-MethodeAnhang A: Lösungen zu allen Übungsaufgaben
                +

                Stichwortverzeichnis

                218 Fachbegriffe. Ein Klick führt zum jeweiligen Kapitel/Abschnitt (statt einer im Web bedeutungslosen Seitenzahl wie im gedruckten Register).

                A

                Absolutbetrag (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Aktivierungsschalter (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                AlternativoptimaAnhang E: Glossar und Literatur

                B

                Bedingte Kopplung (Konjunktion)Anhang B: Katalog der Modellierungsmuster

                C

                D

                DualitätstheorieAnhang E: Glossar und Literatur

                E

                Entweder-Oder-BedingungAnhang B: Katalog der Modellierungsmuster

                F

                Falsche Dualwerte (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                G

                Gestaffelte Preise (stückweise linear)Anhang B: Katalog der Modellierungsmuster
                Gini-KoeffizientProjektwerkstatt

                H

                Herfindahl-IndexProjektwerkstatt

                I

                IIS (Irreducible Infeasible Subset)Anhang C: Fehlerdiagnose-Handbuch
                Implikation (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Importfehler (ortools und highspy)Anhang C: Fehlerdiagnose-Handbuch
                Instabile Lösung (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                J

                K

                Kanonische StandardformAnhang E: Glossar und Literatur
                Kohärentes RisikomaßAnhang E: Glossar und Literatur

                L

                Lagrange-MultiplikatorAnhang E: Glossar und Literatur
                Laufzeitexplosion (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Lineare Programmierung (LP)Anhang E: Glossar und Literatur

                M

                Maximum DrawdownAnhang E: Glossar und Literatur

                N

                Nichtlineare Programmierung (NLP)Anhang E: Glossar und Literatur

                O

                P

                Positiv (semi-)definitAnhang E: Glossar und Literatur

                Q

                R

                S

                T

                TransaktionskostenAnhang E: Glossar und Literatur

                U

                Umrüstkosten (Modellierungsmuster)Anhang B: Katalog der Modellierungsmuster
                Unbounded (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                UnsicherheitsmengeAnhang E: Glossar und Literatur
                Unsinniges Ergebnis (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                V

                Vehicle Routing Problem (VRP)Anhang E: Glossar und Literatur
                Verdächtig guter Backtest (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Vertauschte Spalten (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch

                W

                Weiche Grenze mit StrafkostenAnhang B: Katalog der Modellierungsmuster
                Widersprüchliche Solver (Fehlerbild)Anhang C: Fehlerdiagnose-Handbuch
                Wurzel-Zeit-RegelAnhang E: Glossar und Literatur

                Z

                Zuordnungsproblem (1:1)Anhang B: Katalog der Modellierungsmuster
                Zwei-Phasen-Simplex-MethodeAnhang A: Lösungen zu allen Übungsaufgaben
                diff --git a/OR_HTML_04/unsicherheit.html b/OR_HTML_04/unsicherheit.html index b052fcb..74fd808 100644 --- a/OR_HTML_04/unsicherheit.html +++ b/OR_HTML_04/unsicherheit.html @@ -103,15 +103,16 @@

                📌 Kapitel auf einen Blick

                -

                Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt drei Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.

                +

                Worum geht es? Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit Zufallsgrößen. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist.

                Voraussetzungen: Kapitel 5, Kapitel 11. Grundbegriffe der Statistik (Erwartungswert, Quantil).

                -

                Danach können Sie: 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.

                -

                Zeitbedarf: ca. 5,5 Stunden.

                +

                Danach können Sie: Ein zweistufiges stochastisches Modell aufstellen, Monte-Carlo zur Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht.

                +

                Zeitbedarf: ca. 6,5 Stunden.

                Programme:
                Fluch_des_Durchschnitts.py
                Monte_Carlo.py
                Stochastische_Optimierung.py
                -Robuste_Optimierung.py

                +Robuste_Optimierung.py
                +Chance_Constraints.py

                Notebook: unsicherheit.ipynb
                In Google Colab öffnen

                @@ -220,7 +221,8 @@ q^{*} = \frac{c_{\text{zu wenig}}}{c_{\text{zu wenig}} + c_{\text{zu viel}}}
              3. … ein zweistufiges stochastisches Programm mit Recourse formulieren.
              4. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten.
              5. … ein robustes Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern.
              6. -
              7. … begründen, welcher der drei Ansätze für ein gegebenes Problem passt.
              8. +
              9. … eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern.
              10. +
              11. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt.
              12. … das kritische Verhältnis eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen.
              13. … eine Terminzusage als Quantil formulieren statt als Mittelwert.
              @@ -341,17 +343,18 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.
              <

              🎯 Merksatz Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt.

              -

              Die drei Ansätze im Überblick

              +

              Die vier Ansätze im Überblick

              Abb. 12.1: Ansätze für Optimierung unter Unsicherheit
              ----+++++ @@ -359,6 +362,7 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.< + @@ -367,39 +371,46 @@ nicht beim Mittelwert 225 - typisch fuer asymmetrische Kostenfunktionen.< + + + + + - - - - + + + + +
              Monte-Carlo Stochastische Programmierung Robuste OptimierungChance Constraints
              Verteilung Szenarien mit Wahrscheinlichkeiten nur eine Menge möglicher WerteVerteilung oder Szenarien
              Optimiert nichts (bewertet nur) Erwartungswert über Szenarien Worst Case in der MengeKosten bei zugesicherter Quote
              Typische Frage „Wie riskant ist dieser Plan?“ „Was ist im Mittel am besten?“ „Was hält auch im schlimmsten Fall?“„Was hält in 95 % der Fälle?“
              Stärke beliebige Kennzahlen, sehr flexibel nutzt Wahrscheinlichkeiten voll aus keine Verteilungsannahme nötigdie Zusage steht im Modell
              Schwäche keine Optimierung Wahrscheinlichkeiten müssen stimmen oft übervorsichtig, kostet ErtragZusage gilt nur für die unterstellte Verteilung
              Kapitel9.29.39.4Nachzulesen inAbschnitt 12.4Abschnitt 12.5Abschnitt 12.6Abschnitt 12.7
              +

              Der Unterschied zwischen den letzten beiden ist der, an dem die meisten Diskussionen hängenbleiben. Die robuste Optimierung sagt: „Es darf nie schiefgehen“ — und zahlt dafür. Die Chance Constraint sagt: „Es darf in höchstens 5 % der Fälle schiefgehen“ — und macht damit verhandelbar, was die Sicherheit kosten darf.


              12.4 Monte-Carlo-Simulation

              Monte-Carlo heißt schlicht: Ziehe sehr viele Zufallsszenarien, werte für jedes den Plan aus, und betrachte die Verteilung der Ergebnisse statt eines einzigen Werts.

              @@ -828,34 +839,375 @@ deutlich teurer ist als Leerlauf (5 EUR).

              ⚠️ Der Preis der Robustheit Robuste Modelle sind konservativ. Wer gegen den absoluten Worst Case absichert, zahlt im Normalfall drauf — oft erheblich. Drei Gegenmittel: 1. Unsicherheitsmenge realistisch wählen. Nicht „alles kann passieren“, sondern „Abweichungen bis zu einer Standardabweichung“. 2. Budgeted Uncertainty (Bertsimas/Sim): Man nimmt an, dass höchstens \Gamma von n Parametern gleichzeitig ihren Worst Case annehmen. \Gamma steuert die Vorsicht stufenlos. 3. Nutzen und Preis immer beziffern — genau wie im Programm oben. Ohne diese zwei Zahlen ist die Diskussion „robust oder nicht“ Glaubenssache.


    -

    12.7 Übungsaufgaben

    +

    12.7 Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“

    +

    Die drei bisherigen Ansätze beantworten Fragen, die im Sitzungssaal selten so gestellt werden. „Was ist im Mittel am besten?“ hört ein Vorstand als Ausflucht; „was hält auch im schlimmsten Fall?“ als Einladung, zu viel Geld auszugeben. Gefragt wird fast immer nach einer Wahrscheinlichkeit: Mit welcher Sicherheit hält der Plan?

    +

    Die Wahrscheinlichkeitsbeschränkung schreibt genau das ins Modell:

    +

    +\mathbb{P}\!\left(\mathbf{a}^\top\mathbf{x} \ge b\right) \ge 1 - \alpha +

    +
    +

    📐 Formel-Lesehilfe * \mathbf{a} — der unsichere Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist keine Zahl, sondern eine Zufallsgröße. * \mathbf{x} — Ihre Entscheidung. Sie fällt, bevor \mathbf{a} sich zeigt. * \alpha — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. \alpha = 0{,}05 heißt „in höchstens 5 % der Fälle darf es schiefgehen“.

    +

    Ohne Formel gesagt: „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“

    +
    +

    Das Problem daran. So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen.

    +

    Weg 1: Normalverteilung — die Bedingung wird ein Kegel

    +

    Ist \mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma}), dann ist \mathbf{a}^\top\mathbf{x} für festes \mathbf{x} selbst normalverteilt, mit Erwartungswert \hat{\mathbf{a}}^\top\mathbf{x} und Standardabweichung \sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}. Damit lässt sich die Wahrscheinlichkeit durch ein Quantil ersetzen:

    +

    +\hat{\mathbf{a}}^\top\mathbf{x} \;-\; z_{1-\alpha}\,\lVert \mathbf{L}^\top\mathbf{x}\rVert_2 +\;\ge\; b +\qquad\text{mit}\qquad +\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad +z_{1-\alpha} = \Phi^{-1}(1-\alpha) +

    +

    Das ist eine Second-Order-Cone-Bedingung — dieselbe Bauform, die Kapitel 11 eingeführt hat, und CVXPY löst sie ohne Umstände.

    +
    +

    🎯 Warum eine Norm und keine Summe Der Sicherheitszuschlag \lVert\mathbf{L}^\top\mathbf{x}\rVert_2 ist keine Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken gemeinsam weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen.

    +
    +

    Weg 2: Szenarien — Big-M ohne Verteilungsannahme

    +

    Liegen statt einer Verteilung nur S beobachtete Szenarien vor, bekommt jedes eine Binärvariable z_s, die sagt, ob es verletzt werden darf:

    +

    +\begin{aligned} +\mathbf{a}_s^\top\mathbf{x} &\ge b - M z_s && \forall s\\ +\sum_{s} z_s &\le \alpha S\\ +z_s &\in \{0,1\} +\end{aligned} +

    +

    Aus dem Kegelproblem wird ein MILP mit S Binärvariablen. Für M gilt, was Kapitel 6 über die Big-M-Falle sagt: so klein wie möglich. Hier ist M = b die kleinste gültige Schranke, denn \mathbf{a}_s^\top\mathbf{x} \ge 0 — größer kann eine Verletzung gar nicht ausfallen.

    +

    Der Fall: ein Kraftwerkspark mit 500 MW Zusage

    +
    #!/usr/bin/env python3
    +
    +# Chance_Constraints.py
    +"""
    +Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen.
    +
    +Das Management fragt selten "was ist im Mittel am besten?", sondern "mit
    +welcher Sicherheit haelt der Plan?". Genau das formuliert eine Chance
    +Constraint:  P(Versorgung >= Bedarf) >= 1 - alpha.
    +
    +Gezeigt werden beide Wege dorthin, an derselben Instanz:
    +  (a) analytisch      - unter Normalverteilungsannahme wird daraus eine
    +                        Second-Order-Cone-Bedingung, loesbar mit CVXPY
    +  (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige
    +                        empirische Verteilungen
    +
    +Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt
    +Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung
    +nicht normal ist?
    +
    +Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools
    +noch ein direkter highspy-Import, damit alles in einem Prozess laeuft.
    +"""
    +
    +import cvxpy as cp
    +import numpy as np
    +from scipy.stats import norm
    +
    +# --- Der Kraftwerkspark ----------------------------------------------------
    +# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel
    +# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und
    +# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche,
    +# Genehmigung, Brennstoffversorgung.
    +TECHNIK = ["Gaskraftwerk", "Windpark", "Solarpark", "Biomasse"]
    +KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0])   # EUR je MW und Jahr
    +VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85])
    +STREUUNG = np.array([0.05, 0.16, 0.10, 0.04])
    +GRENZE = np.array([400.0, 250.0, 600.0, 350.0])               # MW
    +
    +# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und
    +# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel
    +# und nicht zu einer Summe unabhaengiger Einzelzuschlaege.
    +KORRELATION = np.array([
    +    [1.00,  0.00,  0.00, 0.05],
    +    [0.00,  1.00, -0.25, 0.00],
    +    [0.00, -0.25,  1.00, 0.00],
    +    [0.05,  0.00,  0.00, 1.00],
    +])
    +SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG)
    +WURZEL = np.linalg.cholesky(SIGMA)          # L mit L @ L.T == SIGMA
    +
    +BEDARF = 500.0          # MW, die gesichert bereitstehen muessen
    +N = len(TECHNIK)
    +
    +# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich.
    +# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das
    +# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt.
    +# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken.
    +P_KAELTEWELLE = 0.08
    +EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88])
    +
    +
    +def mittelwertplan():
    +    """Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig."""
    +    x = cp.Variable(N, nonneg=True)
    +    problem = cp.Problem(cp.Minimize(KOSTEN @ x),
    +                         [VERFUEGBAR @ x >= BEDARF, x <= GRENZE])
    +    problem.solve(solver=cp.CLARABEL)
    +    return problem.value, x.value
    +
    +
    +def chance_constraint_analytisch(alpha):
    +    """
    +    P(a^T x >= BEDARF) >= 1 - alpha  unter a ~ N(VERFUEGBAR, SIGMA).
    +
    +    Aequivalent zu:  VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF
    +    mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der
    +    Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage.
    +    Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken
    +    gemeinsam weniger als jede fuer sich.
    +    """
    +    x = cp.Variable(N, nonneg=True)
    +    z = norm.ppf(1 - alpha)
    +    bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF
    +    problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE])
    +    problem.solve(solver=cp.CLARABEL)
    +    return problem.value, x.value
    +
    +
    +def chance_constraint_szenarien(a, alpha):
    +    """
    +    Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine
    +    Binaervariable z_s, ob es verletzt werden darf.
    +
    +        a_s^T x >= BEDARF - M * z_s        fuer alle s
    +        sum_s z_s <= alpha * S
    +
    +    M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0:
    +    Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M
    +    wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die
    +    Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung.
    +    """
    +    S = len(a)
    +    x = cp.Variable(N, nonneg=True)
    +    z = cp.Variable(S, boolean=True)
    +    problem = cp.Problem(
    +        cp.Minimize(KOSTEN @ x),
    +        [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE])
    +    problem.solve(solver=cp.SCIPY)
    +    return problem.value, x.value
    +
    +
    +def ziehe_wetter(anzahl, seed):
    +    """Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch."""
    +    rng = np.random.default_rng(seed)
    +    a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl)
    +    getroffen = rng.random(anzahl) < P_KAELTEWELLE
    +    a[getroffen] *= EINBRUCH
    +    return np.clip(a, 0.0, 1.0)
    +
    +
    +def sicherheit(x, a):
    +    """Anteil der Szenarien, in denen der Plan den Bedarf deckt."""
    +    return float(np.mean(a @ x >= BEDARF))
    +
    +
    +KOPF = (f"{'Plan':<20} {'Kosten (EUR)':>13}  {'Gas':>4} {'Wind':>4} "
    +        f"{'Sol':>4} {'Bio':>4}  {'Normalwelt':>11} {'echte Welt':>11}")
    +
    +
    +def zeile(name, kosten, x, in_normal, in_echt):
    +    mix = " ".join(f"{w:4.0f}" for w in x)
    +    return (f"{name:<20} {kosten:>13,.0f}  {mix}  "
    +            f"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %")
    +
    +
    +if __name__ == "__main__":
    +    print("=" * 78)
    +    print("  WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK")
    +    print("=" * 78)
    +    print(f"Gesichert bereitzustellen: {BEDARF:.0f} MW\n")
    +    print(f"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} "
    +          f"{'Grenze':>8} {'EUR je erw. MW':>15}")
    +    print("-" * 78)
    +    for i, name in enumerate(TECHNIK):
    +        print(f"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f}  "
    +              f"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} "
    +              f"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}")
    +    print(f"\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, "
    +          f"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.")
    +
    +    # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen -
    +    # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung.
    +    echt = ziehe_wetter(200_000, seed=771)
    +    normalwelt = np.random.default_rng(4711).multivariate_normal(
    +        VERFUEGBAR, SIGMA, size=200_000)
    +
    +    print("\n" + "=" * 78)
    +    print("  (1) Mittelwertplan und Chance Constraints im Vergleich")
    +    print("=" * 78)
    +    print(KOPF)
    +    print("-" * 78)
    +
    +    k_mittel, x_mittel = mittelwertplan()
    +    stufen = [("Mittelwert", k_mittel, sicherheit(x_mittel, normalwelt))]
    +    print(zeile("Mittelwert", k_mittel, x_mittel,
    +                sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt)))
    +
    +    plaene = {}
    +    for alpha in (0.20, 0.10, 0.05, 0.01):
    +        kosten, x = chance_constraint_analytisch(alpha)
    +        plaene[alpha] = (kosten, x)
    +        quote_normal = sicherheit(x, normalwelt)
    +        stufen.append((f"Zusage {(1 - alpha) * 100:.0f} %", kosten, quote_normal))
    +        print(zeile(f"Zusage {(1 - alpha) * 100:.0f} %", kosten, x,
    +                    quote_normal, sicherheit(x, echt)))
    +
    +    print("\n  In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren")
    +    print("  rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).")
    +
    +    print("\n" + "=" * 78)
    +    print("  (2) Was kostet ein Prozentpunkt Versorgungssicherheit?")
    +    print("=" * 78)
    +    print(f"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} "
    +          f"{'EUR je Punkt':>13}")
    +    print("-" * 78)
    +    for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]):
    +        d_punkte = (q2 - q1) * 100
    +        d_kosten = k2 - k1
    +        print(f"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} "
    +              f"{d_kosten / d_punkte:>13,.0f}")
    +    erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100)
    +    letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100)
    +    print(f"\n  Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.")
    +    print("  Sicherheit ist konvex bepreist - genau deshalb muss jemand")
    +    print("  entscheiden, wie viel davon das Unternehmen kaufen will.")
    +
    +    print("\n" + "=" * 78)
    +    print("  (3) Und wenn die Verteilung nicht normal ist?")
    +    print("=" * 78)
    +    k_soc, x_soc = plaene[0.05]
    +    print(f"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in "
    +          f"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern")
    +    print(f"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % "
    +          f"und - entscheidend - auch das Gaskraftwerk nur "
    +          f"{EINBRUCH[0] * 100:.0f} %")
    +    print("des Ueblichen. Alles bricht gleichzeitig ein.\n")
    +
    +    a_bau = ziehe_wetter(400, seed=20260908)
    +    k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05)
    +
    +    print(KOPF)
    +    print("-" * 78)
    +    print(zeile("SOC, Zusage 95 %", k_soc, x_soc,
    +                sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt)))
    +    print(zeile("Szenarien, 95 %", k_sz, x_sz,
    +                sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt)))
    +
    +    print(f"\n  Der SOC-Plan verspricht 95 % und haelt "
    +          f"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist")
    +    print(f"  falsch, sondern die Annahme: In der Normalwelt liefert derselbe")
    +    print(f"  Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.")
    +    print(f"\n  Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} "
    +          f"Binaervariablen) kommt auf")
    +    print(f"  {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer "
    +          f"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:")
    +    print("  Was im Ernstfall ausfaellt, hilft der Zusage nicht.")
    +    print("=" * 78)
    +

    Ausgabe:

    +
    ==============================================================================
    +  WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK
    +==============================================================================
    +Gesichert bereitzustellen: 500 MW
    +
    +Technologie      EUR/MW  verfuegbar  Streuung   Grenze  EUR je erw. MW
    +------------------------------------------------------------------------------
    +Gaskraftwerk     65,000       0.92      0.05     400          70,652
    +Windpark         24,000       0.35      0.16     250          68,571
    +Solarpark        12,000       0.18      0.10     600          66,667
    +Biomasse         60,000       0.85      0.04     350          70,588
    +
    +Vollausbau liefert im Mittel 861 MW, in der Kaeltewelle 552 MW.
    +
    +==============================================================================
    +  (1) Mittelwertplan und Chance Constraints im Vergleich
    +==============================================================================
    +Plan                  Kosten (EUR)   Gas Wind  Sol  Bio   Normalwelt  echte Welt
    +------------------------------------------------------------------------------
    +Mittelwert              34,694,565     8  250  600  350      50.08 %     46.18 %
    +Zusage 80 %             36,382,277   226   25   44  343      80.06 %     73.61 %
    +Zusage 90 %             36,994,777   233   20   35  349      89.99 %     82.82 %
    +Zusage 95 %             37,514,307   241   19   31  350      94.97 %     87.44 %
    +Zusage 99 %             38,529,698   258   18   28  350      98.97 %     91.05 %
    +
    +  In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren
    +  rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).
    +
    +==============================================================================
    +  (2) Was kostet ein Prozentpunkt Versorgungssicherheit?
    +==============================================================================
    +von -> nach                Prozentpunkte    Mehrkosten  EUR je Punkt
    +------------------------------------------------------------------------------
    +Mittelwert -> Zusage 80 %          29.98     1,687,712        56,289
    +Zusage 80 % -> Zusage 90 %          9.93       612,500        61,685
    +Zusage 90 % -> Zusage 95 %          4.98       519,530       104,365
    +Zusage 95 % -> Zusage 99 %          4.00     1,015,390       253,848
    +
    +  Der letzte Prozentpunkt kostet das 4.5-fache des ersten.
    +  Sicherheit ist konvex bepreist - genau deshalb muss jemand
    +  entscheiden, wie viel davon das Unternehmen kaufen will.
    +
    +==============================================================================
    +  (3) Und wenn die Verteilung nicht normal ist?
    +==============================================================================
    +Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in 8 % der Faelle liefern
    +Wind 10 %, Sonne 15 % und - entscheidend - auch das Gaskraftwerk nur 72 %
    +des Ueblichen. Alles bricht gleichzeitig ein.
    +
    +Plan                  Kosten (EUR)   Gas Wind  Sol  Bio   Normalwelt  echte Welt
    +------------------------------------------------------------------------------
    +SOC, Zusage 95 %        37,514,307   241   19   31  350      94.97 %     87.44 %
    +Szenarien, 95 %         43,760,709   350    0    0  350     100.00 %     94.84 %
    +
    +  Der SOC-Plan verspricht 95 % und haelt 87.4 %. Nicht das Verfahren ist
    +  falsch, sondern die Annahme: In der Normalwelt liefert derselbe
    +  Plan 95.0 %.
    +
    +  Der Szenarioplan (400 Szenarien, 400 Binaervariablen) kommt auf
    +  94.8 % - und kostet dafuer +16.7 %. Er kauft keine Windkraft mehr:
    +  Was im Ernstfall ausfaellt, hilft der Zusage nicht.
    +==============================================================================
    +

    Drei Befunde, die man dem Plan nicht ansieht.

    +

    Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus. Das ist kein Fehler, sondern die Definition des Erwartungswerts — und dieselbe Falle wie in Abschnitt 12.3, nur teurer. Er kauft die billigsten Quellen je erwarteter Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, Wind und Sonne schrumpfen auf einen Rest.

    +

    Zweitens: Sicherheit ist konvex bepreist. Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das 4,5-fache. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der nächste Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue.

    +

    Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung. Der Plan verspricht 95 % und hält 87,4 %. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht alles zugleich ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken.

    +

    Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf 94,8 %, kostet dafür 16,7 % mehr — und kauft keine Windkraft mehr. Die Logik dahinter ist hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht.

    +
    +

    ⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich Bei S Szenarien und \alpha = 5\,\% darf das Modell genau 0{,}05 \cdot S davon ignorieren — und es sucht sich die teuersten aus. In der Stichprobe trifft es seine Quote dadurch immer; außerhalb streut sie.

    +

    Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei S = 200, 400, 800, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen 93,1 % und 96,7 %. Die Spanne wurde von S = 200 auf 400 enger (3,5 → 1,2 Prozentpunkte), bei S = 800 aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht.

    +

    Konsequenz für die Praxis: Eine mit Szenarien erkaufte Zusage immer an einer zurückgehaltenen Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in Kapitel 21.

    +
    +
    +

    🎯 Merksatz Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern.

    +
    +
    +

    12.8 Übungsaufgaben

    Lösungen: Abschnitt A.12.

    Aufgabe 12.1 ⭐ — Fluch des Durchschnitts erkennen. Ein Bauunternehmer plant mit der durchschnittlichen Bauzeit von 8 Monaten. Warum ist die erwartete Fertigstellung trotzdem später als 8 Monate, wenn Verzögerungen wahrscheinlicher sind als Beschleunigungen? Nennen Sie zwei weitere Alltagsbeispiele.

    -

    Aufgabe 12.2 ⭐ — Ansatz wählen. 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?

    +

    Aufgabe 12.2 ⭐ — Ansatz wählen. Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? (e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält?

    Aufgabe 12.3 ⭐⭐ — Zweistufiges Modell rechnen. Wiederholen Sie die Handrechnung Warum der Mittelwert in die Irre führt 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?

    Aufgabe 12.4 ⭐⭐ — EVPI interpretieren. 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?

    Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern. Erweitern Sie Monte_Carlo.py: (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.

    -

    Aufgabe 12.6 ⭐⭐⭐ — Budgeted Uncertainty. Erweitern Sie Robuste_Optimierung.py um den Ansatz von Bertsimas/Sim: Höchstens \Gamma Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie \Gamma \in \{0, 1, 2, 3, 4\} und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei \Gamma ganzzahlig genügt es, die \Gamma größten \delta_i w_i abzuziehen — das lässt sich in CVXPY mit cp.sum_largest formulieren.)

    +

    Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen. Chance_Constraints.py beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das szenariobasierte Modell meldet dann bei 95 % infeasible, das analytische dagegen optimal. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten.

    +

    Aufgabe 12.7 ⭐⭐⭐ — Budgeted Uncertainty. Erweitern Sie Robuste_Optimierung.py um den Ansatz von Bertsimas/Sim: Höchstens \Gamma Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie \Gamma \in \{0, 1, 2, 3, 4\} und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei \Gamma ganzzahlig genügt es, die \Gamma größten \delta_i w_i abzuziehen — das lässt sich in CVXPY mit cp.sum_largest formulieren.)


    -

    12.8 Finde den Denkfehler

    +

    12.9 Finde den Denkfehler

    🐛 Finde den Denkfehler 9.1: Warum jedes Projekt zu spät fertig wird

    Ein Projektleiter plant eine Umbaumaßnahme. Fünf Gewerke arbeiten parallel und unabhängig voneinander; fertig ist der Umbau, wenn das letzte fertig ist. Für jedes Gewerk liegt eine Schätzung vor: mindestens 6 Tage, wahrscheinlich 10, im schlechtesten Fall 18.

    Er rechnet: „Jedes Gewerk braucht im Mittel (6 + 10 + 18)/3 = 11{,}3 Tage. Alle laufen gleichzeitig. Also ist der Umbau nach 11,3 Tagen fertig — ich plane großzügig 12 ein.“

    Nachgerechnet mit 200 000 simulierten Umbauten:

    -
    import numpy as np
    -
    -rng = np.random.default_rng(3)
    -dauer = rng.triangular(6, 10, 18, size=(200_000, 5))    # 5 Gewerke, je eine Schaetzung
    -projekt = dauer.max(axis=1)                             # fertig, wenn das LETZTE fertig ist
    -
    -print(f"Mittelwert einer Einzeldauer:  {dauer.mean():.2f} Tage")
    -print(f"Tatsaechliche Projektdauer:    {projekt.mean():.2f} Tage")
    -print(f"90-%-Quantil:                  {np.quantile(projekt, 0.9):.2f} Tage")
    -print(f"Anteil ueber 11,34 Tage:       {(projekt > 11.34).mean() * 100:.1f} %")
    +
    import numpy as np
    +
    +rng = np.random.default_rng(3)
    +dauer = rng.triangular(6, 10, 18, size=(200_000, 5))    # 5 Gewerke, je eine Schaetzung
    +projekt = dauer.max(axis=1)                             # fertig, wenn das LETZTE fertig ist
    +
    +print(f"Mittelwert einer Einzeldauer:  {dauer.mean():.2f} Tage")
    +print(f"Tatsaechliche Projektdauer:    {projekt.mean():.2f} Tage")
    +print(f"90-%-Quantil:                  {np.quantile(projekt, 0.9):.2f} Tage")
    +print(f"Anteil ueber 11,34 Tage:       {(projekt > 11.34).mean() * 100:.1f} %")

    Ausgabe:

    Mittelwert einer Einzeldauer:  11.34 Tage
     Tatsaechliche Projektdauer:    14.38 Tage
    @@ -869,7 +1221,7 @@ Anteil ueber 11,34 Tage:       95.5 %

    🎯 Merksatz Der Fluch des Durchschnitts hat zwei Gesichter. Beim Ersatzteil aus Abschnitt 12.1 führt der Mittelwert in die Irre, weil die Kosten asymmetrisch sind. Hier führt er in die Irre, weil die Verknüpfung asymmetrisch ist: Ein Gewerk, das früher fertig wird, hilft niemandem — ein Gewerk, das sich verspätet, verzögert alles. Beide Male gilt: Der Mittelwert einer Funktion ist nicht die Funktion des Mittelwerts.


    -

    12.9 Micro-Quiz

    +

    12.10 Micro-Quiz

    ❓ Micro-Quiz 9: Drei Fragen zum Selbstcheck

    @@ -880,7 +1232,7 @@ Anteil ueber 11,34 Tage: 95.5 %

    -

    12.10 Selbsttest

    +

    12.11 Selbsttest

    Antworten: Anhang A.

    @@ -890,9 +1242,11 @@ Anteil ueber 11,34 Tage: 95.5 %
  • Was misst der EVPI, und wofür ist er praktisch nützlich?
  • Warum braucht robuste Optimierung keine Wahrscheinlichkeiten?
  • Wie macht man ein Min-Max-Problem mit Box-Unsicherheit lösbar?
  • +
  • Warum wird aus \mathbb{P}(\mathbf{a}^\top\mathbf{x} \ge b) \ge 1-\alpha unter Normalverteilung eine Norm und nicht ein fester Zuschlag je Variable?
  • +
  • Ein Plan mit Chance Constraint verspricht 95 % und hält gemessen 87 %. Woran liegt das — und was ändert die szenariobasierte Formulierung daran?

  • -

    12.11 Zusammenfassung

    +

    12.12 Zusammenfassung

    Ausblick. Kapitel 13 fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung.

    diff --git a/OR_HTML_04/vorwort.html b/OR_HTML_04/vorwort.html index e09ecab..753f40f 100644 --- a/OR_HTML_04/vorwort.html +++ b/OR_HTML_04/vorwort.html @@ -609,141 +609,146 @@ Alles bereit — Sie können mit dem ersten Kapitel beginnen. Kapitel 12 +Chance_Constraints.py +Zusage „mit 95 % Sicherheit“, SOC und Big-M +Kapitel 12 + + Bellman_Minimalbeispiel.py Rückwärtsinduktion Kapitel 13 - + Mehrziel_Pareto.py Pareto-Front, ε-Constraint, Gewichtslücke Kapitel 14 - + Predict_then_Optimize.py MSE gegen Entscheidungskosten Kapitel 15 - + Strukturbruecke.py Derselbe Code über Werkstatt und Depot Kapitel 16 - + Kraftwerkseinsatz.py Unit Commitment unter Windunsicherheit Kapitel 17 - + Mehrperiodige_Order_Execution.py Almgren-Chriss Kapitel 13 - + Renditen_Vergleich.py Diskret vs. logarithmisch Kapitel 18 - + Schaetzrauschen_Demo.py Error-Maximizer messen Kapitel 18 - + Finanzdaten_Ledoit_Wolf.py Datenpipeline, Shrinkage Kapitel 18 - + Kovarianz_Falle.py Singuläre Kovarianz, Error-Maximizer Kapitel 18 - + Markowitz_CVXPY.py GMV, Max Sharpe, Frontier Kapitel 19 - + Diversifikation_Demo.py Korrelation und Portfoliorisiko Kapitel 19 - + Renditeschaetzung_Falle.py Schätzfehler in erwarteten Renditen Kapitel 19 - + VaR_CVaR_Demo.py Fat Tails, Subadditivität Kapitel 20 - + CVaR_Portfolio.py CVaR mit Reibung Kapitel 20 - + QuantitativeTradingEngine.py Walk-Forward-Backtest Kapitel 21 - + Backtest_Fallen.py Fünf Selbsttäuschungen Kapitel 21 - + Data_Snooping.py Bestes aus N Versuchen auf Rauschen Kapitel 21 - + Infeasibility_Diagnose.py Notfallplan statt Fehler Kapitel 22 - + Erklaerbarkeit.py Constraint-Trace, Was-wäre-wenn Kapitel 22 - + Betriebsueberwachung.py Status, Gap und Zeitausschöpfung Kapitel 22 - + or_kern.py Gemeinsamer Unterbau: Domäne, Status, Prüfung Kapitel 22 - + Solverwechsel_CPSAT_HiGHS.py Derselbe Fall in zwei Solvern Kapitel 22 - + test_or_kern.py Testsuite für ein Optimierungsmodell Kapitel 23 - + Mutationstest.py Findet die Lücken der eigenen Tests Kapitel 23 - + Benchmark_Skalierung.py Aufbau gegen Lösen, vier Bibliotheken Kapitel 23 - + Optimierungsdienst.py Das Modell als HTTP-Dienst Kapitel 23 - + Konfliktsuche.py Deletion Filter: den Widerspruch einkreisen Anhang C diff --git a/Operations_Research_mit_Python_Version_04.md b/Operations_Research_mit_Python_Version_04.md index 1c1efc3..121f42c 100644 --- a/Operations_Research_mit_Python_Version_04.md +++ b/Operations_Research_mit_Python_Version_04.md @@ -359,6 +359,7 @@ Alle Beispielprogramme dieses Buchs im Überblick, sortiert nach Kapitel — pra | `Monte_Carlo.py` | Monte-Carlo-Bewertung | [Kapitel 12](#kap-unsicherheit) | | `Stochastische_Optimierung.py` | Two-Stage mit Recourse | [Kapitel 12](#kap-unsicherheit) | | `Robuste_Optimierung.py` | Worst-Case-Absicherung | [Kapitel 12](#kap-unsicherheit) | +| `Chance_Constraints.py` | Zusage „mit 95 % Sicherheit“, SOC und Big-M | [Kapitel 12](#kap-unsicherheit) | | `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion | [Kapitel 13](#kap-dynamische-programmierung) | | `Mehrziel_Pareto.py` | Pareto-Front, ε-Constraint, Gewichtslücke | [Kapitel 14](#kap-mehrziel) | | `Predict_then_Optimize.py` | MSE gegen Entscheidungskosten | [Kapitel 15](#kap-prognose) | @@ -12773,19 +12774,20 @@ else: > **📌 Kapitel auf einen Blick** > -> **Worum geht es?** Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt drei Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist. +> **Worum geht es?** Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen — und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist. > > **Voraussetzungen:** [Kapitel 5](#kap-lp), [Kapitel 11](#kap-qp-nlp). Grundbegriffe der Statistik (Erwartungswert, Quantil). > -> **Danach können Sie:** 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. +> **Danach können Sie:** Ein zweistufiges stochastisches Modell aufstellen, Monte-Carlo zur Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch danebengeht. > -> **Zeitbedarf:** ca. 5,5 Stunden. +> **Zeitbedarf:** ca. 6,5 Stunden. > > **Programme:**\ > `Fluch_des_Durchschnitts.py`\ > `Monte_Carlo.py`\ > `Stochastische_Optimierung.py`\ -> `Robuste_Optimierung.py` +> `Robuste_Optimierung.py`\ +> `Chance_Constraints.py` > > **Notebook:** `Notebooks_04/unsicherheit.ipynb`\ > [In Google Colab öffnen](https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/unsicherheit.ipynb) @@ -12867,9 +12869,10 @@ Nach diesem Kapitel können Sie … 2. … ein **zweistufiges stochastisches Programm** mit Recourse formulieren. 3. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten. 4. … ein **robustes** Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern. -5. … begründen, welcher der drei Ansätze für ein gegebenes Problem passt. -6. … das **kritische Verhältnis** eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen. -7. … eine Terminzusage als Quantil formulieren statt als Mittelwert. +5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis eines Prozentpunkts Sicherheit beziffern. +6. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt. +7. … das **kritische Verhältnis** eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen. +8. … eine Terminzusage als Quantil formulieren statt als Mittelwert. --- @@ -12993,18 +12996,20 @@ Kein Rechentrick, sondern brute-force über alle ganzzahligen $x$ von 0 bis 500 > **🎯 Merksatz** Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit Mittelwerten plant, optimiert eine Welt, die es nicht gibt. -### Die drei Ansätze im Überblick +### Die vier Ansätze im Überblick ![Abb. 12.1: Ansätze für Optimierung unter Unsicherheit](bilder_04/kap09_unsicherheit_ansaetze.svg) -| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | -| --- | --- | --- | --- | -| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | -| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | -| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | -| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | -| Schwäche | keine Optimierung | Wahrscheinlichkeiten müssen stimmen | oft übervorsichtig, kostet Ertrag | -| Kapitel | 9.2 | 9.3 | 9.4 | +| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | **Chance Constraints** | +| --- | --- | --- | --- | --- | +| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | Verteilung **oder** Szenarien | +| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | Kosten bei **zugesicherter Quote** | +| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | „Was hält in 95 % der Fälle?“ | +| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | die Zusage steht **im Modell** | +| Schwäche | keine Optimierung | Wahrscheinlichkeiten müssen stimmen | oft übervorsichtig, kostet Ertrag | Zusage gilt nur für die unterstellte Verteilung | +| Nachzulesen in | [Abschnitt 12.4](#sec:unsicherheit-monte-carlo-simulation) | [Abschnitt 12.5](#sec:unsicherheit-zweistufige-stochastische-programmierung) | [Abschnitt 12.6](#sec:unsicherheit-robuste-optimierung-gegen-den-worst-case) | [Abschnitt 12.7](#sec:unsicherheit-chance-constraints) | + +**Der Unterschied zwischen den letzten beiden** ist der, an dem die meisten Diskussionen hängenbleiben. Die robuste Optimierung sagt: *„Es darf **nie** schiefgehen“* — und zahlt dafür. Die Chance Constraint sagt: *„Es darf in höchstens 5 % der Fälle schiefgehen“* — und macht damit verhandelbar, was die Sicherheit kosten darf. --- @@ -13477,13 +13482,382 @@ if __name__ == "__main__": --- -## 12.7 Übungsaufgaben {#sec:unsicherheit-uebungsaufgaben} +## 12.7 Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“ {#sec:unsicherheit-chance-constraints} + +Die drei bisherigen Ansätze beantworten Fragen, die im Sitzungssaal selten so gestellt werden. „Was ist im Mittel am besten?“ hört ein Vorstand als Ausflucht; „was hält auch im schlimmsten Fall?“ als Einladung, zu viel Geld auszugeben. Gefragt wird fast immer nach einer **Wahrscheinlichkeit**: *Mit welcher Sicherheit hält der Plan?* + +Die **Wahrscheinlichkeitsbeschränkung**`\index{Chance Constraint}`{=latex} schreibt genau das ins Modell: + +$$ +\mathbb{P}\!\left(\mathbf{a}^\top\mathbf{x} \ge b\right) \ge 1 - \alpha +$$ + +> **📐 Formel-Lesehilfe** +> * $\mathbf{a}$ — der **unsichere** Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist keine Zahl, sondern eine Zufallsgröße. +> * $\mathbf{x}$ — Ihre Entscheidung. Sie fällt, **bevor** $\mathbf{a}$ sich zeigt. +> * $\alpha$ — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. $\alpha = 0{,}05$ heißt „in höchstens 5 % der Fälle darf es schiefgehen“. +> +> **Ohne Formel gesagt:** „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, was das kostet.“ + +**Das Problem daran.** So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, was sie über die Verteilung voraussetzen. + +### Weg 1: Normalverteilung — die Bedingung wird ein Kegel + +Ist $\mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma})$, dann ist $\mathbf{a}^\top\mathbf{x}$ für festes $\mathbf{x}$ selbst normalverteilt, mit Erwartungswert $\hat{\mathbf{a}}^\top\mathbf{x}$ und Standardabweichung $\sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}$. Damit lässt sich die Wahrscheinlichkeit durch ein Quantil ersetzen: + +$$ +\hat{\mathbf{a}}^\top\mathbf{x} \;-\; z_{1-\alpha}\,\lVert \mathbf{L}^\top\mathbf{x}\rVert_2 +\;\ge\; b +\qquad\text{mit}\qquad +\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad +z_{1-\alpha} = \Phi^{-1}(1-\alpha) +$$ + +Das ist eine **Second-Order-Cone-Bedingung**`\index{Konische Optimierung (SOCP, SDP)}`{=latex} — dieselbe Bauform, die [Kapitel 11](#kap-qp-nlp) eingeführt hat, und CVXPY löst sie ohne Umstände. + +> **🎯 Warum eine Norm und keine Summe** Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und würde Diversifikation nicht belohnen. + +### Weg 2: Szenarien — Big-M ohne Verteilungsannahme + +Liegen statt einer Verteilung nur $S$ beobachtete Szenarien vor, bekommt jedes eine Binärvariable $z_s$, die sagt, ob es verletzt werden **darf**: + +$$ +\begin{aligned} +\mathbf{a}_s^\top\mathbf{x} &\ge b - M z_s && \forall s\\ +\sum_{s} z_s &\le \alpha S\\ +z_s &\in \{0,1\} +\end{aligned} +$$ + +Aus dem Kegelproblem wird ein MILP mit $S$ Binärvariablen. Für $M$ gilt, was [Kapitel 6](#kap-milp) über die Big-M-Falle sagt: **so klein wie möglich**. Hier ist $M = b$ die kleinste gültige Schranke, denn $\mathbf{a}_s^\top\mathbf{x} \ge 0$ — größer kann eine Verletzung gar nicht ausfallen. + +### Der Fall: ein Kraftwerkspark mit 500 MW Zusage + +```python +#!/usr/bin/env python3 + +# Chance_Constraints.py +""" +Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen. + +Das Management fragt selten "was ist im Mittel am besten?", sondern "mit +welcher Sicherheit haelt der Plan?". Genau das formuliert eine Chance +Constraint: P(Versorgung >= Bedarf) >= 1 - alpha. + +Gezeigt werden beide Wege dorthin, an derselben Instanz: + (a) analytisch - unter Normalverteilungsannahme wird daraus eine + Second-Order-Cone-Bedingung, loesbar mit CVXPY + (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige + empirische Verteilungen + +Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt +Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung +nicht normal ist? + +Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools +noch ein direkter highspy-Import, damit alles in einem Prozess laeuft. +""" + +import cvxpy as cp +import numpy as np +from scipy.stats import norm + +# --- Der Kraftwerkspark ---------------------------------------------------- +# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel +# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und +# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche, +# Genehmigung, Brennstoffversorgung. +TECHNIK = ["Gaskraftwerk", "Windpark", "Solarpark", "Biomasse"] +KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr +VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85]) +STREUUNG = np.array([0.05, 0.16, 0.10, 0.04]) +GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW + +# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und +# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel +# und nicht zu einer Summe unabhaengiger Einzelzuschlaege. +KORRELATION = np.array([ + [1.00, 0.00, 0.00, 0.05], + [0.00, 1.00, -0.25, 0.00], + [0.00, -0.25, 1.00, 0.00], + [0.05, 0.00, 0.00, 1.00], +]) +SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG) +WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA + +BEDARF = 500.0 # MW, die gesichert bereitstehen muessen +N = len(TECHNIK) + +# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich. +# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das +# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt. +# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken. +P_KAELTEWELLE = 0.08 +EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88]) + + +def mittelwertplan(): + """Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.""" + x = cp.Variable(N, nonneg=True) + problem = cp.Problem(cp.Minimize(KOSTEN @ x), + [VERFUEGBAR @ x >= BEDARF, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_analytisch(alpha): + """ + P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA). + + Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF + mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der + Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage. + Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken + gemeinsam weniger als jede fuer sich. + """ + x = cp.Variable(N, nonneg=True) + z = norm.ppf(1 - alpha) + bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF + problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_szenarien(a, alpha): + """ + Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine + Binaervariable z_s, ob es verletzt werden darf. + + a_s^T x >= BEDARF - M * z_s fuer alle s + sum_s z_s <= alpha * S + + M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0: + Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M + wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die + Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung. + """ + S = len(a) + x = cp.Variable(N, nonneg=True) + z = cp.Variable(S, boolean=True) + problem = cp.Problem( + cp.Minimize(KOSTEN @ x), + [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE]) + problem.solve(solver=cp.SCIPY) + return problem.value, x.value + + +def ziehe_wetter(anzahl, seed): + """Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.""" + rng = np.random.default_rng(seed) + a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl) + getroffen = rng.random(anzahl) < P_KAELTEWELLE + a[getroffen] *= EINBRUCH + return np.clip(a, 0.0, 1.0) + + +def sicherheit(x, a): + """Anteil der Szenarien, in denen der Plan den Bedarf deckt.""" + return float(np.mean(a @ x >= BEDARF)) + + +KOPF = (f"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} " + f"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}") + + +def zeile(name, kosten, x, in_normal, in_echt): + mix = " ".join(f"{w:4.0f}" for w in x) + return (f"{name:<20} {kosten:>13,.0f} {mix} " + f"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %") + + +if __name__ == "__main__": + print("=" * 78) + print(" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK") + print("=" * 78) + print(f"Gesichert bereitzustellen: {BEDARF:.0f} MW\n") + print(f"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} " + f"{'Grenze':>8} {'EUR je erw. MW':>15}") + print("-" * 78) + for i, name in enumerate(TECHNIK): + print(f"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} " + f"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} " + f"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}") + print(f"\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, " + f"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.") + + # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen - + # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung. + echt = ziehe_wetter(200_000, seed=771) + normalwelt = np.random.default_rng(4711).multivariate_normal( + VERFUEGBAR, SIGMA, size=200_000) + + print("\n" + "=" * 78) + print(" (1) Mittelwertplan und Chance Constraints im Vergleich") + print("=" * 78) + print(KOPF) + print("-" * 78) + + k_mittel, x_mittel = mittelwertplan() + stufen = [("Mittelwert", k_mittel, sicherheit(x_mittel, normalwelt))] + print(zeile("Mittelwert", k_mittel, x_mittel, + sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt))) + + plaene = {} + for alpha in (0.20, 0.10, 0.05, 0.01): + kosten, x = chance_constraint_analytisch(alpha) + plaene[alpha] = (kosten, x) + quote_normal = sicherheit(x, normalwelt) + stufen.append((f"Zusage {(1 - alpha) * 100:.0f} %", kosten, quote_normal)) + print(zeile(f"Zusage {(1 - alpha) * 100:.0f} %", kosten, x, + quote_normal, sicherheit(x, echt))) + + print("\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren") + print(" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).") + + print("\n" + "=" * 78) + print(" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?") + print("=" * 78) + print(f"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} " + f"{'EUR je Punkt':>13}") + print("-" * 78) + for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]): + d_punkte = (q2 - q1) * 100 + d_kosten = k2 - k1 + print(f"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} " + f"{d_kosten / d_punkte:>13,.0f}") + erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100) + letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100) + print(f"\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.") + print(" Sicherheit ist konvex bepreist - genau deshalb muss jemand") + print(" entscheiden, wie viel davon das Unternehmen kaufen will.") + + print("\n" + "=" * 78) + print(" (3) Und wenn die Verteilung nicht normal ist?") + print("=" * 78) + k_soc, x_soc = plaene[0.05] + print(f"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in " + f"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern") + print(f"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % " + f"und - entscheidend - auch das Gaskraftwerk nur " + f"{EINBRUCH[0] * 100:.0f} %") + print("des Ueblichen. Alles bricht gleichzeitig ein.\n") + + a_bau = ziehe_wetter(400, seed=20260908) + k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05) + + print(KOPF) + print("-" * 78) + print(zeile("SOC, Zusage 95 %", k_soc, x_soc, + sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt))) + print(zeile("Szenarien, 95 %", k_sz, x_sz, + sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt))) + + print(f"\n Der SOC-Plan verspricht 95 % und haelt " + f"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist") + print(f" falsch, sondern die Annahme: In der Normalwelt liefert derselbe") + print(f" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.") + print(f"\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} " + f"Binaervariablen) kommt auf") + print(f" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer " + f"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:") + print(" Was im Ernstfall ausfaellt, hilft der Zusage nicht.") + print("=" * 78) +``` + +**Ausgabe:** + +``` +============================================================================== + WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK +============================================================================== +Gesichert bereitzustellen: 500 MW + +Technologie EUR/MW verfuegbar Streuung Grenze EUR je erw. MW +------------------------------------------------------------------------------ +Gaskraftwerk 65,000 0.92 0.05 400 70,652 +Windpark 24,000 0.35 0.16 250 68,571 +Solarpark 12,000 0.18 0.10 600 66,667 +Biomasse 60,000 0.85 0.04 350 70,588 + +Vollausbau liefert im Mittel 861 MW, in der Kaeltewelle 552 MW. + +============================================================================== + (1) Mittelwertplan und Chance Constraints im Vergleich +============================================================================== +Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt +------------------------------------------------------------------------------ +Mittelwert 34,694,565 8 250 600 350 50.08 % 46.18 % +Zusage 80 % 36,382,277 226 25 44 343 80.06 % 73.61 % +Zusage 90 % 36,994,777 233 20 35 349 89.99 % 82.82 % +Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 % +Zusage 99 % 38,529,698 258 18 28 350 98.97 % 91.05 % + + In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren + rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3). + +============================================================================== + (2) Was kostet ein Prozentpunkt Versorgungssicherheit? +============================================================================== +von -> nach Prozentpunkte Mehrkosten EUR je Punkt +------------------------------------------------------------------------------ +Mittelwert -> Zusage 80 % 29.98 1,687,712 56,289 +Zusage 80 % -> Zusage 90 % 9.93 612,500 61,685 +Zusage 90 % -> Zusage 95 % 4.98 519,530 104,365 +Zusage 95 % -> Zusage 99 % 4.00 1,015,390 253,848 + + Der letzte Prozentpunkt kostet das 4.5-fache des ersten. + Sicherheit ist konvex bepreist - genau deshalb muss jemand + entscheiden, wie viel davon das Unternehmen kaufen will. + +============================================================================== + (3) Und wenn die Verteilung nicht normal ist? +============================================================================== +Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in 8 % der Faelle liefern +Wind 10 %, Sonne 15 % und - entscheidend - auch das Gaskraftwerk nur 72 % +des Ueblichen. Alles bricht gleichzeitig ein. + +Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt +------------------------------------------------------------------------------ +SOC, Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 % +Szenarien, 95 % 43,760,709 350 0 0 350 100.00 % 94.84 % + + Der SOC-Plan verspricht 95 % und haelt 87.4 %. Nicht das Verfahren ist + falsch, sondern die Annahme: In der Normalwelt liefert derselbe + Plan 95.0 %. + + Der Szenarioplan (400 Szenarien, 400 Binaervariablen) kommt auf + 94.8 % - und kostet dafuer +16.7 %. Er kauft keine Windkraft mehr: + Was im Ernstfall ausfaellt, hilft der Zusage nicht. +============================================================================== +``` + +**Drei Befunde, die man dem Plan nicht ansieht.** + +**Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus.** Das ist kein Fehler, sondern die Definition des Erwartungswerts — und dieselbe Falle wie in [Abschnitt 12.3](#sec:unsicherheit-der-fluch-des-durchschnitts), nur teurer. Er kauft die billigsten Quellen je *erwarteter* Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, Wind und Sonne schrumpfen auf einen Rest. + +**Zweitens: Sicherheit ist konvex bepreist.** Die ersten dreißig Prozentpunkte kosten rund 56 000 € pro Punkt, die letzten vier rund 254 000 € — das **4,5-fache**. Das ist die Zahl, die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der *nächste* Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins Blaue. + +**Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verfahren falsch rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken. + +Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf 94,8 %, kostet dafür 16,7 % mehr — und kauft **keine Windkraft mehr**. Die Logik dahinter ist hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht. + +> **⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich** Bei $S$ Szenarien und $\alpha = 5\,\%$ darf das Modell genau $0{,}05 \cdot S$ davon ignorieren — und es sucht sich die **teuersten** aus. In der Stichprobe trifft es seine Quote dadurch immer; außerhalb streut sie. +> +> Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei $S = 200$, $400$, $800$, geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen **93,1 % und 96,7 %**. Die Spanne wurde von $S = 200$ auf $400$ enger (3,5 → 1,2 Prozentpunkte), bei $S = 800$ aber wieder breiter (3,1) — die Zahl der Szenarien allein schließt die Lücke also nicht. +> +> **Konsequenz für die Praxis:** Eine mit Szenarien erkaufte Zusage immer an einer **zurückgehaltenen** Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim Backtest in [Kapitel 21](#kap-handelsmaschine). + +> **🎯 Merksatz** Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern. + +--- + +## 12.8 Übungsaufgaben {#sec:unsicherheit-uebungsaufgaben} > Lösungen: [Abschnitt A.12](#sec:loesungen-unsicherheit). **Aufgabe 12.1 ⭐ — Fluch des Durchschnitts erkennen.** Ein Bauunternehmer plant mit der *durchschnittlichen* Bauzeit von 8 Monaten. Warum ist die *erwartete* Fertigstellung trotzdem später als 8 Monate, wenn Verzögerungen wahrscheinlicher sind als Beschleunigungen? Nennen Sie zwei weitere Alltagsbeispiele. -**Aufgabe 12.2 ⭐ — Ansatz wählen.** 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? +**Aufgabe 12.2 ⭐ — Ansatz wählen.** Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? (e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält? **Aufgabe 12.3 ⭐⭐ — Zweistufiges Modell rechnen.** Wiederholen Sie die Handrechnung *Warum der Mittelwert in die Irre führt* 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? @@ -13491,11 +13865,13 @@ if __name__ == "__main__": **Aufgabe 12.5 ⭐⭐⭐ — Monte-Carlo erweitern.** Erweitern Sie `Monte_Carlo.py`: (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. -**Aufgabe 12.6 ⭐⭐⭐ — Budgeted Uncertainty.** Erweitern Sie `Robuste_Optimierung.py` um den Ansatz von Bertsimas/Sim: Höchstens $\Gamma$ Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie $\Gamma \in \{0, 1, 2, 3, 4\}$ und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei $\Gamma$ ganzzahlig genügt es, die $\Gamma$ größten $\delta_i w_i$ abzuziehen — das lässt sich in CVXPY mit `cp.sum_largest` formulieren.) +**Aufgabe 12.6 ⭐⭐ — Den Preis der Zusage selbst bestimmen.** `Chance_Constraints.py` beziffert die Kosten für 80, 90, 95 und 99 %. (a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. Was passiert mit dem Anlagenmix? (b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das **szenariobasierte** Modell meldet dann bei 95 % `infeasible`, das **analytische** dagegen `optimal`. Erklären Sie den Unterschied. Welchem der beiden würden Sie glauben? (c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, was Sie ihm mit dieser Kurve antworten. + +**Aufgabe 12.7 ⭐⭐⭐ — Budgeted Uncertainty.** Erweitern Sie `Robuste_Optimierung.py` um den Ansatz von Bertsimas/Sim: Höchstens $\Gamma$ Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie $\Gamma \in \{0, 1, 2, 3, 4\}$ und stellen Sie den Verlauf von Ertrag und Absicherung dar. (Hinweis: Bei $\Gamma$ ganzzahlig genügt es, die $\Gamma$ größten $\delta_i w_i$ abzuziehen — das lässt sich in CVXPY mit `cp.sum_largest` formulieren.) --- -## 12.8 Finde den Denkfehler {#sec:unsicherheit-denkfehler} +## 12.9 Finde den Denkfehler {#sec:unsicherheit-denkfehler} > **🐛 Finde den Denkfehler 9.1: Warum jedes Projekt zu spät fertig wird** > @@ -13535,7 +13911,7 @@ if __name__ == "__main__": --- -## 12.9 Micro-Quiz {#sec:unsicherheit-quiz} +## 12.10 Micro-Quiz {#sec:unsicherheit-quiz} > **❓ Micro-Quiz 9: Drei Fragen zum Selbstcheck** > @@ -13549,7 +13925,7 @@ if __name__ == "__main__": --- -## 12.10 Selbsttest {#sec:unsicherheit-selbsttest} +## 12.11 Selbsttest {#sec:unsicherheit-selbsttest} > Antworten: [Anhang A](#selbsttest-loesung-unsicherheit). @@ -13558,10 +13934,12 @@ if __name__ == "__main__": 3. Was misst der EVPI, und wofür ist er praktisch nützlich? 4. Warum braucht robuste Optimierung keine Wahrscheinlichkeiten? 5. Wie macht man ein Min-Max-Problem mit Box-Unsicherheit lösbar? +6. Warum wird aus $\mathbb{P}(\mathbf{a}^\top\mathbf{x} \ge b) \ge 1-\alpha$ unter Normalverteilung eine **Norm** und nicht ein fester Zuschlag je Variable? +7. Ein Plan mit Chance Constraint verspricht 95 % und hält gemessen 87 %. Woran liegt das — und was ändert die szenariobasierte Formulierung daran? --- -## 12.11 Zusammenfassung {#sec:unsicherheit-zusammenfassung} +## 12.12 Zusammenfassung {#sec:unsicherheit-zusammenfassung} * **Der Fluch des Durchschnitts** ist kein Randphänomen und hat zwei Gesichter. Bei asymmetrischen **Kosten** liegt das Optimum systematisch neben dem Mittelwert — das Newsvendor-Verhältnis $c_{-}/(c_{-}+c_{+})$ sagt, wie weit. Bei asymmetrischer **Verknüpfung** (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). * **Eine Terminzusage ist ein Quantil, kein Mittelwert.** Wer den Erwartungswert zusagt, reißt den Termin bei fünf parallelen Vorgängen in 95 % der Fälle. @@ -13569,6 +13947,9 @@ if __name__ == "__main__": * **Zweistufige stochastische Programme** trennen die Festlegung von der Reaktion — ein $\mathbf{x}$, aber je Szenario ein $\mathbf{y}_s$. * **Der EVPI** begrenzt, was eine perfekte Prognose wert sein darf. * **Robuste Optimierung** braucht keine Wahrscheinlichkeiten, nur eine Unsicherheitsmenge — und wird für einfache Mengen zu einem gewöhnlichen Problem mit Abzugsterm. +* **Chance Constraints** schreiben die Zusage selbst ins Modell. Unter Normalverteilung wird daraus eine **Kegelbedingung** (der Sicherheitszuschlag ist eine Norm, deshalb belohnt sie Mischung), szenariobasiert ein **MILP mit Big-M** ohne jede Verteilungsannahme. +* **Sicherheit ist konvex bepreist.** Im Beispiel kostet der letzte Prozentpunkt das 4,5-fache des ersten. Die richtige Managementfrage lautet nicht „was kostet Sicherheit“, sondern „was kostet der *nächste* Prozentpunkt“. +* **Eine Zusage gilt nur für die unterstellte Verteilung.** Ein Plan, der unter Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle nicht kannte, in der *alles* zugleich ausfällt. Szenariobasierte Zusagen wiederum überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden. * **Beziffern Sie immer Preis und Nutzen der Absicherung.** Ohne beide Zahlen ist die Entscheidung nicht begründbar. **Ausblick.** [Kapitel 13](#kap-dynamische-programmierung) fügt die Zeitdimension hinzu: Entscheidungen, die über viele Perioden aufeinander aufbauen — gelöst mit der Bellman-Gleichung. @@ -24556,7 +24937,7 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R **9.1 — Fluch des Durchschnitts.** Weil sich Verzögerungen **fortpflanzen** und die Verteilung rechtsschief ist: Ein Gewerk, das früher fertig wird, beschleunigt selten den Gesamtablauf (der nächste Schritt ist noch nicht bereit), ein verspätetes verzögert alles. Weitere Beispiele: Wartezeiten in Warteschlangen (nichtlinear in der Auslastung), Projektkosten mit Nachträgen. -**9.2 — Ansatz wählen.** (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). +**9.2 — Ansatz wählen.** (a) robust (Ausfall ist existenziell, Wahrscheinlichkeiten unzuverlässig). (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). (e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der Erwartungswert zu schwach (er sagt nichts über die Zusage). **9.3 — Zweistufig rechnen.** (a) Mit Spot = 60: Ableitung bei $x \in (100,250)$: $40 - 60\cdot0{,}5 + 5\cdot0{,}5 = 40-30+2{,}5 = +12{,}5 > 0$ → Kapazität **senken**. Bei $x < 100$: $40 - 60 = -20 < 0$ → erhöhen. Optimum daher $x^* = 100$. (b) Billige Nachbesserung macht Vorhalten unattraktiv. (c) Bei $x = 225$ optimal müsste die Ableitung dort das Vorzeichen wechseln — das ist bei diskreten Szenarien nur zufällig der Fall; der Mittelwert ist nur bei symmetrischen Kosten und stetiger Verteilung optimal. @@ -24564,7 +24945,9 @@ Der Ausweg auf `scipy` hat das Warnsystem umgangen, nicht das Problem gelöst. R **9.5 — Monte-Carlo erweitern.** (a) Servicelevel 95 %: Kapazität = 95 %-Quantil des Bedarfs (ca. 485). Die Zusatzkosten gegenüber dem Kostenoptimum (180) betragen ein Vielfaches — Servicelevel ist teuer, und genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (b) `cvar = kosten[kosten >= np.quantile(kosten, 0.95)].mean()`. (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben. -**9.6 — Budgeted Uncertainty.** +**9.6 — Den Preis der Zusage selbst bestimmen.** (a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die Norm, tragen zur gesicherten Leistung aber kaum bei. (b) Der Unterschied ist **nicht** numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert selbst der **Vollausbau** in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese Fälle in den Daten und meldet korrekt `infeasible`, sobald die Zusage über 92 % steigt. Das analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet `optimal` — und der Plan hält gemessen 87,4 %. Glauben sollte man dem Szenariomodell: Ein `infeasible` ist hier die **richtige** Antwort. Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. (c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“ + +**9.7 — Budgeted Uncertainty.** ```python abzug = cp.sum_largest(cp.multiply(UNSICHERHEIT, w), Gamma) ziel = cp.Maximize(MU_SCHAETZUNG @ w - abzug - 0.5*LAMBDA*cp.quad_form(w, SIGMA)) @@ -24628,6 +25011,8 @@ Und die Formulierung gegenüber dem Auftraggeber lautet nicht „der Umbau dauer 3. Die Differenz zwischen Kosten unter Unsicherheit und Kosten bei perfektem Wissen — eine **Obergrenze** für den Wert jeder Prognoseverbesserung. 4. Weil sie nur eine **Menge** möglicher Werte braucht, nicht deren Verteilung — sie optimiert gegen das schlechteste Element dieser Menge. 5. Für einfache Mengen (Box, Ellipsoid) lässt sich das innere Maximum geschlossen ausrechnen und wird zu einem Abzugsterm; das Gesamtproblem bleibt konvex. +6. Weil die Streuung der Summe $\mathbf{a}^\top\mathbf{x}$ nicht die Summe der Streuungen ist: $\sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}$ enthält die **Kovarianzen**. Ein fester Zuschlag je Variable wäre linear und würde deshalb übersehen, dass sich gegenläufige Größen teilweise aufheben — die Norm belohnt Mischung, ein linearer Aufschlag nicht. +7. Weil die Zusage nur so gut ist wie die unterstellte Verteilung. Die Normalverteilung kennt keine Ereignisse, in denen alle Quellen **gleichzeitig** ausfallen; ihre schwachen Korrelationen unterschätzen genau den Fall, der die Zusage bricht. Die szenariobasierte Formulierung hat diese Fälle in den Daten und trifft die Quote — erkauft mit Mehrkosten und mit dem Risiko, sich an die verwendete Stichprobe anzupassen. --- diff --git a/Operations_Research_mit_Python_Version_04.pdf b/Operations_Research_mit_Python_Version_04.pdf index 3c608b7..d5e8848 100644 Binary files a/Operations_Research_mit_Python_Version_04.pdf and b/Operations_Research_mit_Python_Version_04.pdf differ diff --git a/Operations_Research_mit_Python_Version_04/00_Vorwort_und_Lesehilfe.md b/Operations_Research_mit_Python_Version_04/00_Vorwort_und_Lesehilfe.md index 9d6ced9..aabc7f4 100644 --- a/Operations_Research_mit_Python_Version_04/00_Vorwort_und_Lesehilfe.md +++ b/Operations_Research_mit_Python_Version_04/00_Vorwort_und_Lesehilfe.md @@ -449,6 +449,7 @@ Pfad E oder um gezielt nach einem Thema zu suchen: | `Monte_Carlo.py` | Monte-Carlo-Bewertung | {ref:kap:unsicherheit} | | `Stochastische_Optimierung.py` | Two-Stage mit Recourse | {ref:kap:unsicherheit} | | `Robuste_Optimierung.py` | Worst-Case-Absicherung | {ref:kap:unsicherheit} | +| `Chance_Constraints.py` | Zusage „mit 95 % Sicherheit“, SOC und Big-M | {ref:kap:unsicherheit} | | `Bellman_Minimalbeispiel.py` | Rückwärtsinduktion | {ref:kap:dynamische-programmierung} | | `Mehrziel_Pareto.py` | Pareto-Front, ε-Constraint, Gewichtslücke | {ref:kap:mehrziel} | | `Predict_then_Optimize.py` | MSE gegen Entscheidungskosten | {ref:kap:prognose} | diff --git a/Operations_Research_mit_Python_Version_04/31_Unsicherheit.md b/Operations_Research_mit_Python_Version_04/31_Unsicherheit.md index 84932ee..8855759 100644 --- a/Operations_Research_mit_Python_Version_04/31_Unsicherheit.md +++ b/Operations_Research_mit_Python_Version_04/31_Unsicherheit.md @@ -3,22 +3,25 @@ > **📌 Kapitel auf einen Blick** > > **Worum geht es?** Bisher waren alle Parameter bekannt. In der Realität sind Nachfrage, -> Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt drei Wege, damit umzugehen — +> Rendite und Fahrzeit **Zufallsgrößen**. Dieses Kapitel zeigt vier Wege, damit umzugehen — > und warum „einfach den Mittelwert einsetzen“ der schlechteste davon ist. > > **Voraussetzungen:** {ref:kap:lp}, {ref:kap:qp-nlp}. Grundbegriffe der Statistik (Erwartungswert, Quantil). > > **Danach können Sie:** 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. +> Risikomessung einsetzen, ein robustes Modell für den Worst Case formulieren, eine +> Wahrscheinlichkeitszusage („mit 95 % Sicherheit“) als lösbare Nebenbedingung schreiben — +> und erkennen, wann Rechnen mit Mittelwerten zulässig ist und wann es systematisch +> danebengeht. > -> **Zeitbedarf:** ca. 5,5 Stunden. +> **Zeitbedarf:** ca. 6,5 Stunden. > > **Programme:**\ > `Fluch_des_Durchschnitts.py`\ > `Monte_Carlo.py`\ > `Stochastische_Optimierung.py`\ -> `Robuste_Optimierung.py` +> `Robuste_Optimierung.py`\ +> `Chance_Constraints.py` --- @@ -112,10 +115,13 @@ Nach diesem Kapitel können Sie … 2. … ein **zweistufiges stochastisches Programm** mit Recourse formulieren. 3. … Monte-Carlo einsetzen, um Verteilungen statt Punktschätzungen zu bewerten. 4. … ein **robustes** Modell mit Unsicherheitsmenge aufstellen und seinen Preis beziffern. -5. … begründen, welcher der drei Ansätze für ein gegebenes Problem passt. -6. … das **kritische Verhältnis** eines Newsvendor-Problems aufstellen und daraus die +5. … eine **Wahrscheinlichkeitszusage** („mit 95 % Sicherheit“) als Chance Constraint + schreiben — analytisch als Kegelbedingung oder szenariobasiert mit Big-M — und den Preis + eines Prozentpunkts Sicherheit beziffern. +6. … begründen, welcher der vier Ansätze für ein gegebenes Problem passt. +7. … das **kritische Verhältnis** eines Newsvendor-Problems aufstellen und daraus die optimale Bestellmenge als Quantil ablesen. -7. … eine Terminzusage als Quantil formulieren statt als Mittelwert. +8. … eine Terminzusage als Quantil formulieren statt als Mittelwert. --- @@ -256,18 +262,23 @@ Erwartungswert der Kosten ist größer als die Kosten des Erwartungswerts. > Der Durchschnittskunde kauft nie. Das Durchschnittsszenario tritt nie ein. Wer mit > Mittelwerten plant, optimiert eine Welt, die es nicht gibt. -### Die drei Ansätze im Überblick +### Die vier Ansätze im Überblick ![Ansätze für Optimierung unter Unsicherheit](bilder_04/kap09_unsicherheit_ansaetze.svg) -| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | -| --- | --- | --- | --- | -| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | -| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | -| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | -| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | -| Schwäche | keine Optimierung | Wahrscheinlichkeiten müssen stimmen | oft übervorsichtig, kostet Ertrag | -| Nachzulesen in | {ref:sec:unsicherheit-monte-carlo-simulation} | {ref:sec:unsicherheit-zweistufige-stochastische-programmierung} | {ref:sec:unsicherheit-robuste-optimierung-gegen-den-worst-case} | +| | **Monte-Carlo** | **Stochastische Programmierung** | **Robuste Optimierung** | **Chance Constraints** | +| --- | --- | --- | --- | --- | +| Braucht | Verteilung | Szenarien **mit Wahrscheinlichkeiten** | nur eine **Menge** möglicher Werte | Verteilung **oder** Szenarien | +| Optimiert | nichts (bewertet nur) | **Erwartungswert** über Szenarien | **Worst Case** in der Menge | Kosten bei **zugesicherter Quote** | +| Typische Frage | „Wie riskant ist dieser Plan?“ | „Was ist im Mittel am besten?“ | „Was hält auch im schlimmsten Fall?“ | „Was hält in 95 % der Fälle?“ | +| Stärke | beliebige Kennzahlen, sehr flexibel | nutzt Wahrscheinlichkeiten voll aus | keine Verteilungsannahme nötig | die Zusage steht **im Modell** | +| Schwäche | keine Optimierung | Wahrscheinlichkeiten müssen stimmen | oft übervorsichtig, kostet Ertrag | Zusage gilt nur für die unterstellte Verteilung | +| Nachzulesen in | {ref:sec:unsicherheit-monte-carlo-simulation} | {ref:sec:unsicherheit-zweistufige-stochastische-programmierung} | {ref:sec:unsicherheit-robuste-optimierung-gegen-den-worst-case} | {ref:sec:unsicherheit-chance-constraints} | + +**Der Unterschied zwischen den letzten beiden** ist der, an dem die meisten Diskussionen +hängenbleiben. Die robuste Optimierung sagt: *„Es darf **nie** schiefgehen“* — und zahlt +dafür. Die Chance Constraint sagt: *„Es darf in höchstens 5 % der Fälle schiefgehen“* — und +macht damit verhandelbar, was die Sicherheit kosten darf. --- @@ -791,6 +802,427 @@ if __name__ == "__main__": --- +## Wahrscheinlichkeitsbeschränkungen: „mit 95 % Sicherheit“ {#sec:unsicherheit-chance-constraints} + +Die drei bisherigen Ansätze beantworten Fragen, die im Sitzungssaal selten so gestellt +werden. „Was ist im Mittel am besten?“ hört ein Vorstand als Ausflucht; „was hält auch im +schlimmsten Fall?“ als Einladung, zu viel Geld auszugeben. Gefragt wird fast immer nach einer +**Wahrscheinlichkeit**: *Mit welcher Sicherheit hält der Plan?* + +Die **Wahrscheinlichkeitsbeschränkung**{idx:Chance Constraint} schreibt genau das ins Modell: + +$$ +\mathbb{P}\!\left(\mathbf{a}^\top\mathbf{x} \ge b\right) \ge 1 - \alpha +$$ + +> **📐 Formel-Lesehilfe** +> * $\mathbf{a}$ — der **unsichere** Vektor: Verfügbarkeiten, Erträge, Fahrzeiten. Er ist +> keine Zahl, sondern eine Zufallsgröße. +> * $\mathbf{x}$ — Ihre Entscheidung. Sie fällt, **bevor** $\mathbf{a}$ sich zeigt. +> * $\alpha$ — die Ausfallwahrscheinlichkeit, die Sie akzeptieren. $\alpha = 0{,}05$ heißt +> „in höchstens 5 % der Fälle darf es schiefgehen“. +> +> **Ohne Formel gesagt:** „Plane so, dass die Zusage in 95 von 100 Fällen hält — und sag mir, +> was das kostet.“ + +**Das Problem daran.** So aufgeschrieben ist die Bedingung nicht lösbar: Ein Solver kann +nichts mit einem Wahrscheinlichkeitsoperator anfangen. Es braucht einen Weg, sie in etwas zu +übersetzen, das ein Optimierer versteht. Es gibt zwei, und sie unterscheiden sich in dem, +was sie über die Verteilung voraussetzen. + +### Weg 1: Normalverteilung — die Bedingung wird ein Kegel + +Ist $\mathbf{a} \sim \mathcal{N}(\hat{\mathbf{a}}, \boldsymbol{\Sigma})$, dann ist +$\mathbf{a}^\top\mathbf{x}$ für festes $\mathbf{x}$ selbst normalverteilt, mit Erwartungswert +$\hat{\mathbf{a}}^\top\mathbf{x}$ und Standardabweichung +$\sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}$. Damit lässt sich die Wahrscheinlichkeit +durch ein Quantil ersetzen: + +$$ +\hat{\mathbf{a}}^\top\mathbf{x} \;-\; z_{1-\alpha}\,\lVert \mathbf{L}^\top\mathbf{x}\rVert_2 +\;\ge\; b +\qquad\text{mit}\qquad +\boldsymbol{\Sigma} = \mathbf{L}\mathbf{L}^\top,\quad +z_{1-\alpha} = \Phi^{-1}(1-\alpha) +$$ + +Das ist eine **Second-Order-Cone-Bedingung**{idx:Konische Optimierung (SOCP, SDP)} — dieselbe +Bauform, die {ref:kap:qp-nlp} eingeführt hat, und CVXPY löst sie ohne Umstände. + +> **🎯 Warum eine Norm und keine Summe** +> Der Sicherheitszuschlag $\lVert\mathbf{L}^\top\mathbf{x}\rVert_2$ ist **keine** Summe von +> Einzelzuschlägen je Anlage. Genau darin steckt der Nutzen der Mischung: Zwei gegenläufige +> Quellen schwanken **gemeinsam** weniger als jede für sich, und die Norm rechnet das +> automatisch mit. Ein fester Aufschlag je Anlage könnte das nicht — er wäre linear und +> würde Diversifikation nicht belohnen. + +### Weg 2: Szenarien — Big-M ohne Verteilungsannahme + +Liegen statt einer Verteilung nur $S$ beobachtete Szenarien vor, bekommt jedes eine +Binärvariable $z_s$, die sagt, ob es verletzt werden **darf**: + +$$ +\begin{aligned} +\mathbf{a}_s^\top\mathbf{x} &\ge b - M z_s && \forall s\\ +\sum_{s} z_s &\le \alpha S\\ +z_s &\in \{0,1\} +\end{aligned} +$$ + +Aus dem Kegelproblem wird ein MILP mit $S$ Binärvariablen. Für $M$ gilt, was +{ref:kap:milp} über die Big-M-Falle sagt: **so klein wie möglich**. Hier ist $M = b$ die +kleinste gültige Schranke, denn $\mathbf{a}_s^\top\mathbf{x} \ge 0$ — größer kann eine +Verletzung gar nicht ausfallen. + +### Der Fall: ein Kraftwerkspark mit 500 MW Zusage + +```python +#!/usr/bin/env python3 + +# Chance_Constraints.py +""" +Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen. + +Das Management fragt selten "was ist im Mittel am besten?", sondern "mit +welcher Sicherheit haelt der Plan?". Genau das formuliert eine Chance +Constraint: P(Versorgung >= Bedarf) >= 1 - alpha. + +Gezeigt werden beide Wege dorthin, an derselben Instanz: + (a) analytisch - unter Normalverteilungsannahme wird daraus eine + Second-Order-Cone-Bedingung, loesbar mit CVXPY + (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige + empirische Verteilungen + +Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt +Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung +nicht normal ist? + +Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools +noch ein direkter highspy-Import, damit alles in einem Prozess laeuft. +""" + +import cvxpy as cp +import numpy as np +from scipy.stats import norm + +# --- Der Kraftwerkspark ---------------------------------------------------- +# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel +# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und +# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche, +# Genehmigung, Brennstoffversorgung. +TECHNIK = ["Gaskraftwerk", "Windpark", "Solarpark", "Biomasse"] +KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr +VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85]) +STREUUNG = np.array([0.05, 0.16, 0.10, 0.04]) +GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW + +# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und +# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel +# und nicht zu einer Summe unabhaengiger Einzelzuschlaege. +KORRELATION = np.array([ + [1.00, 0.00, 0.00, 0.05], + [0.00, 1.00, -0.25, 0.00], + [0.00, -0.25, 1.00, 0.00], + [0.05, 0.00, 0.00, 1.00], +]) +SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG) +WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA + +BEDARF = 500.0 # MW, die gesichert bereitstehen muessen +N = len(TECHNIK) + +# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich. +# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das +# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt. +# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken. +P_KAELTEWELLE = 0.08 +EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88]) + + +def mittelwertplan(): + """Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.""" + x = cp.Variable(N, nonneg=True) + problem = cp.Problem(cp.Minimize(KOSTEN @ x), + [VERFUEGBAR @ x >= BEDARF, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_analytisch(alpha): + """ + P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA). + + Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF + mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der + Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage. + Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken + gemeinsam weniger als jede fuer sich. + """ + x = cp.Variable(N, nonneg=True) + z = norm.ppf(1 - alpha) + bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF + problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_szenarien(a, alpha): + """ + Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine + Binaervariable z_s, ob es verletzt werden darf. + + a_s^T x >= BEDARF - M * z_s fuer alle s + sum_s z_s <= alpha * S + + M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0: + Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M + wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die + Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung. + """ + S = len(a) + x = cp.Variable(N, nonneg=True) + z = cp.Variable(S, boolean=True) + problem = cp.Problem( + cp.Minimize(KOSTEN @ x), + [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE]) + problem.solve(solver=cp.SCIPY) + return problem.value, x.value + + +def ziehe_wetter(anzahl, seed): + """Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.""" + rng = np.random.default_rng(seed) + a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl) + getroffen = rng.random(anzahl) < P_KAELTEWELLE + a[getroffen] *= EINBRUCH + return np.clip(a, 0.0, 1.0) + + +def sicherheit(x, a): + """Anteil der Szenarien, in denen der Plan den Bedarf deckt.""" + return float(np.mean(a @ x >= BEDARF)) + + +KOPF = (f"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} " + f"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}") + + +def zeile(name, kosten, x, in_normal, in_echt): + mix = " ".join(f"{w:4.0f}" for w in x) + return (f"{name:<20} {kosten:>13,.0f} {mix} " + f"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %") + + +if __name__ == "__main__": + print("=" * 78) + print(" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK") + print("=" * 78) + print(f"Gesichert bereitzustellen: {BEDARF:.0f} MW\n") + print(f"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} " + f"{'Grenze':>8} {'EUR je erw. MW':>15}") + print("-" * 78) + for i, name in enumerate(TECHNIK): + print(f"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} " + f"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} " + f"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}") + print(f"\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, " + f"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.") + + # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen - + # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung. + echt = ziehe_wetter(200_000, seed=771) + normalwelt = np.random.default_rng(4711).multivariate_normal( + VERFUEGBAR, SIGMA, size=200_000) + + print("\n" + "=" * 78) + print(" (1) Mittelwertplan und Chance Constraints im Vergleich") + print("=" * 78) + print(KOPF) + print("-" * 78) + + k_mittel, x_mittel = mittelwertplan() + stufen = [("Mittelwert", k_mittel, sicherheit(x_mittel, normalwelt))] + print(zeile("Mittelwert", k_mittel, x_mittel, + sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt))) + + plaene = {} + for alpha in (0.20, 0.10, 0.05, 0.01): + kosten, x = chance_constraint_analytisch(alpha) + plaene[alpha] = (kosten, x) + quote_normal = sicherheit(x, normalwelt) + stufen.append((f"Zusage {(1 - alpha) * 100:.0f} %", kosten, quote_normal)) + print(zeile(f"Zusage {(1 - alpha) * 100:.0f} %", kosten, x, + quote_normal, sicherheit(x, echt))) + + print("\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren") + print(" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).") + + print("\n" + "=" * 78) + print(" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?") + print("=" * 78) + print(f"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} " + f"{'EUR je Punkt':>13}") + print("-" * 78) + for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]): + d_punkte = (q2 - q1) * 100 + d_kosten = k2 - k1 + print(f"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} " + f"{d_kosten / d_punkte:>13,.0f}") + erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100) + letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100) + print(f"\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.") + print(" Sicherheit ist konvex bepreist - genau deshalb muss jemand") + print(" entscheiden, wie viel davon das Unternehmen kaufen will.") + + print("\n" + "=" * 78) + print(" (3) Und wenn die Verteilung nicht normal ist?") + print("=" * 78) + k_soc, x_soc = plaene[0.05] + print(f"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in " + f"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern") + print(f"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % " + f"und - entscheidend - auch das Gaskraftwerk nur " + f"{EINBRUCH[0] * 100:.0f} %") + print("des Ueblichen. Alles bricht gleichzeitig ein.\n") + + a_bau = ziehe_wetter(400, seed=20260908) + k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05) + + print(KOPF) + print("-" * 78) + print(zeile("SOC, Zusage 95 %", k_soc, x_soc, + sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt))) + print(zeile("Szenarien, 95 %", k_sz, x_sz, + sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt))) + + print(f"\n Der SOC-Plan verspricht 95 % und haelt " + f"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist") + print(f" falsch, sondern die Annahme: In der Normalwelt liefert derselbe") + print(f" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.") + print(f"\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} " + f"Binaervariablen) kommt auf") + print(f" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer " + f"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:") + print(" Was im Ernstfall ausfaellt, hilft der Zusage nicht.") + print("=" * 78) +``` + +**Ausgabe:** + +``` +============================================================================== + WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK +============================================================================== +Gesichert bereitzustellen: 500 MW + +Technologie EUR/MW verfuegbar Streuung Grenze EUR je erw. MW +------------------------------------------------------------------------------ +Gaskraftwerk 65,000 0.92 0.05 400 70,652 +Windpark 24,000 0.35 0.16 250 68,571 +Solarpark 12,000 0.18 0.10 600 66,667 +Biomasse 60,000 0.85 0.04 350 70,588 + +Vollausbau liefert im Mittel 861 MW, in der Kaeltewelle 552 MW. + +============================================================================== + (1) Mittelwertplan und Chance Constraints im Vergleich +============================================================================== +Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt +------------------------------------------------------------------------------ +Mittelwert 34,694,565 8 250 600 350 50.08 % 46.18 % +Zusage 80 % 36,382,277 226 25 44 343 80.06 % 73.61 % +Zusage 90 % 36,994,777 233 20 35 349 89.99 % 82.82 % +Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 % +Zusage 99 % 38,529,698 258 18 28 350 98.97 % 91.05 % + + In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren + rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3). + +============================================================================== + (2) Was kostet ein Prozentpunkt Versorgungssicherheit? +============================================================================== +von -> nach Prozentpunkte Mehrkosten EUR je Punkt +------------------------------------------------------------------------------ +Mittelwert -> Zusage 80 % 29.98 1,687,712 56,289 +Zusage 80 % -> Zusage 90 % 9.93 612,500 61,685 +Zusage 90 % -> Zusage 95 % 4.98 519,530 104,365 +Zusage 95 % -> Zusage 99 % 4.00 1,015,390 253,848 + + Der letzte Prozentpunkt kostet das 4.5-fache des ersten. + Sicherheit ist konvex bepreist - genau deshalb muss jemand + entscheiden, wie viel davon das Unternehmen kaufen will. + +============================================================================== + (3) Und wenn die Verteilung nicht normal ist? +============================================================================== +Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in 8 % der Faelle liefern +Wind 10 %, Sonne 15 % und - entscheidend - auch das Gaskraftwerk nur 72 % +des Ueblichen. Alles bricht gleichzeitig ein. + +Plan Kosten (EUR) Gas Wind Sol Bio Normalwelt echte Welt +------------------------------------------------------------------------------ +SOC, Zusage 95 % 37,514,307 241 19 31 350 94.97 % 87.44 % +Szenarien, 95 % 43,760,709 350 0 0 350 100.00 % 94.84 % + + Der SOC-Plan verspricht 95 % und haelt 87.4 %. Nicht das Verfahren ist + falsch, sondern die Annahme: In der Normalwelt liefert derselbe + Plan 95.0 %. + + Der Szenarioplan (400 Szenarien, 400 Binaervariablen) kommt auf + 94.8 % - und kostet dafuer +16.7 %. Er kauft keine Windkraft mehr: + Was im Ernstfall ausfaellt, hilft der Zusage nicht. +============================================================================== +``` + +**Drei Befunde, die man dem Plan nicht ansieht.** + +**Erstens: Der Mittelwertplan fällt in der Hälfte aller Fälle aus.** Das ist kein Fehler, +sondern die Definition des Erwartungswerts — und dieselbe Falle wie in +{ref:sec:unsicherheit-der-fluch-des-durchschnitts}, nur teurer. Er kauft die billigsten +Quellen je *erwarteter* Megawattstunde und maximiert Wind und Sonne bis an die Ausbaugrenze. +Sobald die Zusage im Modell steht, dreht sich das Bild: Gas und Biomasse tragen den Plan, +Wind und Sonne schrumpfen auf einen Rest. + +**Zweitens: Sicherheit ist konvex bepreist.** Die ersten dreißig Prozentpunkte kosten rund +56 000 € pro Punkt, die letzten vier rund 254 000 € — das **4,5-fache**. Das ist die Zahl, +die in die Vorlage gehört: Nicht „was kostet Versorgungssicherheit“, sondern „was kostet der +*nächste* Prozentpunkt“. Wer 99,9 % fordert, ohne diese Kurve gesehen zu haben, fordert ins +Blaue. + +**Drittens — und das ist der unangenehme Befund: Die Zusage gilt nur für die unterstellte +Verteilung.** Der Plan verspricht 95 % und hält **87,4 %**. Nicht weil das Verfahren falsch +rechnet: In einer reinen Normalwelt liefert derselbe Plan exakt seine 95,0 %. Sondern weil +die Normalverteilung eine Kältewelle mit Dunkelflaute nicht kennt. In der bricht **alles +zugleich** ein — auch das Gaskraftwerk, weil bei Frost der Netzdruck fällt. Eine +Kovarianzmatrix mit Korrelationen um null kann so etwas nicht ausdrücken. + +Der szenariobasierte Weg kennt diese Fälle, weil sie in den Daten stehen. Er kommt auf +94,8 %, kostet dafür 16,7 % mehr — und kauft **keine Windkraft mehr**. Die Logik dahinter ist +hart und richtig: Was im Ernstfall ausfällt, hilft der Zusage nicht. + +> **⚠️ Die Szenariomethode überanpasst — und mehr Szenarien helfen nicht verlässlich** +> Bei $S$ Szenarien und $\alpha = 5\,\%$ darf das Modell genau $0{,}05 \cdot S$ davon +> ignorieren — und es sucht sich die **teuersten** aus. In der Stichprobe trifft es seine +> Quote dadurch immer; außerhalb streut sie. +> +> Gemessen an zwölf Läufen dieses Modells (je vier Ziehungen bei $S = 200$, $400$, $800$, +> geprüft an 200 000 unabhängigen Szenarien): Die tatsächliche Quote lag zwischen +> **93,1 % und 96,7 %**. Die Spanne wurde von $S = 200$ auf $400$ enger (3,5 → 1,2 +> Prozentpunkte), bei $S = 800$ aber wieder breiter (3,1) — die Zahl der Szenarien allein +> schließt die Lücke also nicht. +> +> **Konsequenz für die Praxis:** Eine mit Szenarien erkaufte Zusage immer an einer +> **zurückgehaltenen** Stichprobe nachmessen, so wie es das Programm oben tut. Wer nur die +> Trefferquote in den Baudaten berichtet, berichtet eine Tautologie — dieselbe Falle wie beim +> Backtest in {ref:kap:handelsmaschine}. + +> **🎯 Merksatz** +> Die robuste Optimierung fragt „was, wenn alles schiefgeht?“, die Chance Constraint „wie oft +> darf es schiefgehen?“. Nur die zweite Frage hat eine Antwort, über die man verhandeln +> kann — und nur sie zwingt dazu, den Preis eines Prozentpunkts Sicherheit zu beziffern. + +--- + ## Übungsaufgaben {#sec:unsicherheit-uebungsaufgaben} > Lösungen: {ref:sec:loesungen-unsicherheit}. @@ -801,11 +1233,12 @@ Ein Bauunternehmer plant mit der *durchschnittlichen* Bauzeit von 8 Monaten. War sind als Beschleunigungen? Nennen Sie zwei weitere Alltagsbeispiele. **Aufgabe ⭐ — Ansatz wählen.** -Welcher der drei Ansätze passt? Begründen Sie: +Welcher der vier Ansätze passt? Begründen Sie: (a) Wie viele Notstromaggregate für ein Krankenhaus? (b) Wie viel Weizen einkaufen bei bekannter Preisverteilung? (c) Wie hoch ein Deich gebaut werden muss? (d) Wie viele Saisonkräfte einstellen bei bekannten Nachfrageszenarien? +(e) Wie viel Speicherkapazität, damit ein Gasnetz an 99 % aller Wintertage hält? **Aufgabe ⭐⭐ — Zweistufiges Modell rechnen.** Wiederholen Sie die Handrechnung *Warum der Mittelwert in die Irre führt* mit geänderten Kosten: Spot 60 € statt 120 €. @@ -824,6 +1257,16 @@ Erweitern Sie `Monte_Carlo.py`: (b) Berechnen Sie den CVaR der Kosten (Mittelwert der schlechtesten 5 %). (c) Zeichnen Sie ein Histogramm der Kosten für drei Kapazitäten. +**Aufgabe ⭐⭐ — Den Preis der Zusage selbst bestimmen.** +`Chance_Constraints.py` beziffert die Kosten für 80, 90, 95 und 99 %. +(a) Ergänzen Sie die Stufen 99,5 % und 99,9 % und setzen Sie die Kosten je Prozentpunkt fort. + Was passiert mit dem Anlagenmix? +(b) Drosseln Sie die Ausbaugrenze der Biomasse von 350 auf 200 MW. Das **szenariobasierte** + Modell meldet dann bei 95 % `infeasible`, das **analytische** dagegen `optimal`. Erklären + Sie den Unterschied. Welchem der beiden würden Sie glauben? +(c) Ein Vorstand fordert „99,99 % Versorgungssicherheit“. Formulieren Sie in drei Sätzen, + was Sie ihm mit dieser Kurve antworten. + **Aufgabe ⭐⭐⭐ — Budgeted Uncertainty.** Erweitern Sie `Robuste_Optimierung.py` um den Ansatz von Bertsimas/Sim: Höchstens $\Gamma$ Titel nehmen gleichzeitig ihren Worst Case an. Variieren Sie @@ -931,6 +1374,10 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.) 3. Was misst der EVPI, und wofür ist er praktisch nützlich? 4. Warum braucht robuste Optimierung keine Wahrscheinlichkeiten? 5. Wie macht man ein Min-Max-Problem mit Box-Unsicherheit lösbar? +6. Warum wird aus $\mathbb{P}(\mathbf{a}^\top\mathbf{x} \ge b) \ge 1-\alpha$ unter + Normalverteilung eine **Norm** und nicht ein fester Zuschlag je Variable? +7. Ein Plan mit Chance Constraint verspricht 95 % und hält gemessen 87 %. Woran liegt das — + und was ändert die szenariobasierte Formulierung daran? --- @@ -951,6 +1398,16 @@ das lässt sich in CVXPY mit `cp.sum_largest` formulieren.) * **Der EVPI** begrenzt, was eine perfekte Prognose wert sein darf. * **Robuste Optimierung** braucht keine Wahrscheinlichkeiten, nur eine Unsicherheitsmenge — und wird für einfache Mengen zu einem gewöhnlichen Problem mit Abzugsterm. +* **Chance Constraints** schreiben die Zusage selbst ins Modell. Unter Normalverteilung wird + daraus eine **Kegelbedingung** (der Sicherheitszuschlag ist eine Norm, deshalb belohnt sie + Mischung), szenariobasiert ein **MILP mit Big-M** ohne jede Verteilungsannahme. +* **Sicherheit ist konvex bepreist.** Im Beispiel kostet der letzte Prozentpunkt das + 4,5-fache des ersten. Die richtige Managementfrage lautet nicht „was kostet Sicherheit“, + sondern „was kostet der *nächste* Prozentpunkt“. +* **Eine Zusage gilt nur für die unterstellte Verteilung.** Ein Plan, der unter + Normalverteilung 95 % verspricht, hielt gemessen 87 %, weil das Modell die Kältewelle + nicht kannte, in der *alles* zugleich ausfällt. Szenariobasierte Zusagen wiederum + überanpassen — beide müssen an zurückgehaltenen Daten nachgemessen werden. * **Beziffern Sie immer Preis und Nutzen der Absicherung.** Ohne beide Zahlen ist die Entscheidung nicht begründbar. diff --git a/Operations_Research_mit_Python_Version_04/90_Anhang_Loesungen.md b/Operations_Research_mit_Python_Version_04/90_Anhang_Loesungen.md index e5e8b9b..bdcd9c0 100644 --- a/Operations_Research_mit_Python_Version_04/90_Anhang_Loesungen.md +++ b/Operations_Research_mit_Python_Version_04/90_Anhang_Loesungen.md @@ -1492,6 +1492,9 @@ Projektkosten mit Nachträgen. (b) stochastisch (Verteilung bekannt). (c) robust bzw. Extremwertstatistik (seltene Ereignisse, katastrophale Folgen). (d) stochastisch (Szenarien mit Wahrscheinlichkeiten liegen vor). +(e) Chance Constraint — die Zusage ist bereits als Quote formuliert („an 99 % aller +Wintertage“). Robust wäre hier zu teuer (es gibt immer einen kälteren Tag), der +Erwartungswert zu schwach (er sagt nichts über die Zusage). **9.3 — Zweistufig rechnen.** (a) Mit Spot = 60: Ableitung bei $x \in (100,250)$: $40 - 60\cdot0{,}5 + 5\cdot0{,}5 = @@ -1514,7 +1517,27 @@ genau diese Zahl braucht die Geschäftsleitung für die Entscheidung. (c) Erwartetes Bild: Bei kleiner Kapazität ist die Kostenverteilung stark rechtsschief (seltene, teure Notzukäufe); bei großer Kapazität schmal und nach rechts verschoben. -**9.6 — Budgeted Uncertainty.** +**9.6 — Den Preis der Zusage selbst bestimmen.** +(a) Die Kosten je Prozentpunkt steigen weiter steil an; der Mix verschiebt sich dabei fast +nur noch zugunsten des Gaskraftwerks, weil Biomasse längst an ihrer Ausbaugrenze liegt. +Wind und Sonne bleiben bei einem kleinen Rest — sie senken über ihre Gegenläufigkeit die +Norm, tragen zur gesicherten Leistung aber kaum bei. +(b) Der Unterschied ist **nicht** numerisch, sondern inhaltlich. Bei 200 MW Biomasse liefert +selbst der **Vollausbau** in der Kältewelle nur rund 440 MW — 7,5 % der Szenarien sind +schlicht nicht bedienbar, egal wie viel Geld man ausgibt. Das Szenariomodell sieht diese +Fälle in den Daten und meldet korrekt `infeasible`, sobald die Zusage über 92 % steigt. Das +analytische Modell kennt sie nicht: Für eine Normalverteilung ist jede Zusage unter 100 % +erfüllbar, wenn man nur genug Streuung wegkauft. Es meldet `optimal` — und der Plan hält +gemessen 87,4 %. +Glauben sollte man dem Szenariomodell: Ein `infeasible` ist hier die **richtige** Antwort. +Es sagt, dass die Zusage nicht am Budget scheitert, sondern am Kraftwerkspark, und dass +nicht mehr Geld hilft, sondern nur eine andere Anlage oder eine kleinere Zusage. +(c) Sinngemäß: „99,99 % kosten nicht 5 % mehr als 99 %, sondern ein Vielfaches — der letzte +Prozentpunkt ist bereits 4,5-mal so teuer wie der erste. Sagen Sie mir, was ein Ausfalltag +das Unternehmen kostet, dann rechne ich aus, welche Quote sich lohnt. Und die Zusage gilt +nur für die Wetterlagen, die wir modelliert haben — die Kältewelle gehört hinein.“ + +**9.7 — Budgeted Uncertainty.** ```python abzug = cp.sum_largest(cp.multiply(UNSICHERHEIT, w), Gamma) ziel = cp.Maximize(MU_SCHAETZUNG @ w - abzug - 0.5*LAMBDA*cp.quad_form(w, SIGMA)) @@ -1625,6 +1648,16 @@ Fall, in dem es tatsächlich zulässig ist. optimiert gegen das schlechteste Element dieser Menge. 5. Für einfache Mengen (Box, Ellipsoid) lässt sich das innere Maximum geschlossen ausrechnen und wird zu einem Abzugsterm; das Gesamtproblem bleibt konvex. +6. Weil die Streuung der Summe $\mathbf{a}^\top\mathbf{x}$ nicht die Summe der Streuungen + ist: $\sqrt{\mathbf{x}^\top\boldsymbol{\Sigma}\mathbf{x}}$ enthält die **Kovarianzen**. + Ein fester Zuschlag je Variable wäre linear und würde deshalb übersehen, dass sich + gegenläufige Größen teilweise aufheben — die Norm belohnt Mischung, ein linearer + Aufschlag nicht. +7. Weil die Zusage nur so gut ist wie die unterstellte Verteilung. Die Normalverteilung + kennt keine Ereignisse, in denen alle Quellen **gleichzeitig** ausfallen; ihre schwachen + Korrelationen unterschätzen genau den Fall, der die Zusage bricht. Die szenariobasierte + Formulierung hat diese Fälle in den Daten und trifft die Quote — erkauft mit Mehrkosten + und mit dem Risiko, sich an die verwendete Stichprobe anzupassen. --- diff --git a/Operations_Research_mit_Python_Version_04_Programme/Chance_Constraints.py b/Operations_Research_mit_Python_Version_04_Programme/Chance_Constraints.py new file mode 100644 index 0000000..6cd90c1 --- /dev/null +++ b/Operations_Research_mit_Python_Version_04_Programme/Chance_Constraints.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 + +# Chance_Constraints.py +""" +Kapitel Unsicherheit: Wahrscheinlichkeitsbeschraenkungen. + +Das Management fragt selten "was ist im Mittel am besten?", sondern "mit +welcher Sicherheit haelt der Plan?". Genau das formuliert eine Chance +Constraint: P(Versorgung >= Bedarf) >= 1 - alpha. + +Gezeigt werden beide Wege dorthin, an derselben Instanz: + (a) analytisch - unter Normalverteilungsannahme wird daraus eine + Second-Order-Cone-Bedingung, loesbar mit CVXPY + (b) szenariobasiert - Big-M mit Binaervariablen, fuer beliebige + empirische Verteilungen + +Und die beiden Messungen, auf die es ankommt: Was kostet ein Prozentpunkt +Versorgungssicherheit - und haelt die Zusage auch dann, wenn die Verteilung +nicht normal ist? + +Solver: CLARABEL (Kegel) und SciPy/HiGHS (gemischt-ganzzahlig). Weder ortools +noch ein direkter highspy-Import, damit alles in einem Prozess laeuft. +""" + +import cvxpy as cp +import numpy as np +from scipy.stats import norm + +# --- Der Kraftwerkspark ---------------------------------------------------- +# Die Verfuegbarkeit ist der Anteil, den eine installierte MW im Mittel +# wirklich liefert: bei Wind und Sonne klein und stark schwankend, bei Gas und +# Biomasse gross und stabil. Die Ausbaugrenze ist der Standort - Flaeche, +# Genehmigung, Brennstoffversorgung. +TECHNIK = ["Gaskraftwerk", "Windpark", "Solarpark", "Biomasse"] +KOSTEN = np.array([65_000.0, 24_000.0, 12_000.0, 60_000.0]) # EUR je MW und Jahr +VERFUEGBAR = np.array([0.92, 0.35, 0.18, 0.85]) +STREUUNG = np.array([0.05, 0.16, 0.10, 0.04]) +GRENZE = np.array([400.0, 250.0, 600.0, 350.0]) # MW + +# Wind und Sonne sind leicht gegenlaeufig: Ein Tiefdruckgebiet bringt Wind und +# Wolken zugleich. Genau diese Korrelation macht die Bedingung zu einem Kegel +# und nicht zu einer Summe unabhaengiger Einzelzuschlaege. +KORRELATION = np.array([ + [1.00, 0.00, 0.00, 0.05], + [0.00, 1.00, -0.25, 0.00], + [0.00, -0.25, 1.00, 0.00], + [0.05, 0.00, 0.00, 1.00], +]) +SIGMA = np.diag(STREUUNG) @ KORRELATION @ np.diag(STREUUNG) +WURZEL = np.linalg.cholesky(SIGMA) # L mit L @ L.T == SIGMA + +BEDARF = 500.0 # MW, die gesichert bereitstehen muessen +N = len(TECHNIK) + +# Die Kaeltewelle mit Dunkelflaute: selten, aber sie trifft alles zugleich. +# Wind und Sonne brechen fast vollstaendig weg - und, das ist der Punkt, das +# Gaskraftwerk liefert ebenfalls weniger, weil bei Frost der Netzdruck faellt. +# Eine Kovarianzmatrix mit Korrelationen um null kann das nicht ausdruecken. +P_KAELTEWELLE = 0.08 +EINBRUCH = np.array([0.72, 0.10, 0.15, 0.88]) + + +def mittelwertplan(): + """Plant mit den Erwartungswerten - ignoriert die Streuung vollstaendig.""" + x = cp.Variable(N, nonneg=True) + problem = cp.Problem(cp.Minimize(KOSTEN @ x), + [VERFUEGBAR @ x >= BEDARF, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_analytisch(alpha): + """ + P(a^T x >= BEDARF) >= 1 - alpha unter a ~ N(VERFUEGBAR, SIGMA). + + Aequivalent zu: VERFUEGBAR^T x - z * ||L^T x||_2 >= BEDARF + mit z = Phi^-1(1 - alpha). Das ist eine Second-Order-Cone-Bedingung: Der + Sicherheitszuschlag ist eine Norm ueber x, kein fester Aufschlag je Anlage. + Deshalb belohnt sie Mischung - zwei gegenlaeufige Quellen schwanken + gemeinsam weniger als jede fuer sich. + """ + x = cp.Variable(N, nonneg=True) + z = norm.ppf(1 - alpha) + bedingung = VERFUEGBAR @ x - z * cp.norm(WURZEL.T @ x, 2) >= BEDARF + problem = cp.Problem(cp.Minimize(KOSTEN @ x), [bedingung, x <= GRENZE]) + problem.solve(solver=cp.CLARABEL) + return problem.value, x.value + + +def chance_constraint_szenarien(a, alpha): + """ + Dieselbe Zusage ohne Verteilungsannahme: Fuer jedes Szenario s sagt eine + Binaervariable z_s, ob es verletzt werden darf. + + a_s^T x >= BEDARF - M * z_s fuer alle s + sum_s z_s <= alpha * S + + M = BEDARF ist die kleinstmoegliche gueltige Schranke, denn a_s^T x >= 0: + Groesser kann eine Verletzung gar nicht ausfallen. Ein unnoetig grosses M + wuerde die LP-Relaxierung aufweichen und die Suche verlangsamen - die + Big-M-Falle aus dem Kapitel Gemischt-ganzzahlige Optimierung. + """ + S = len(a) + x = cp.Variable(N, nonneg=True) + z = cp.Variable(S, boolean=True) + problem = cp.Problem( + cp.Minimize(KOSTEN @ x), + [a @ x >= BEDARF - BEDARF * z, cp.sum(z) <= alpha * S, x <= GRENZE]) + problem.solve(solver=cp.SCIPY) + return problem.value, x.value + + +def ziehe_wetter(anzahl, seed): + """Mischverteilung: normales Wetter, mit P_KAELTEWELLE ein Einbruch.""" + rng = np.random.default_rng(seed) + a = rng.multivariate_normal(VERFUEGBAR, SIGMA, size=anzahl) + getroffen = rng.random(anzahl) < P_KAELTEWELLE + a[getroffen] *= EINBRUCH + return np.clip(a, 0.0, 1.0) + + +def sicherheit(x, a): + """Anteil der Szenarien, in denen der Plan den Bedarf deckt.""" + return float(np.mean(a @ x >= BEDARF)) + + +KOPF = (f"{'Plan':<20} {'Kosten (EUR)':>13} {'Gas':>4} {'Wind':>4} " + f"{'Sol':>4} {'Bio':>4} {'Normalwelt':>11} {'echte Welt':>11}") + + +def zeile(name, kosten, x, in_normal, in_echt): + mix = " ".join(f"{w:4.0f}" for w in x) + return (f"{name:<20} {kosten:>13,.0f} {mix} " + f"{in_normal * 100:>9.2f} % {in_echt * 100:>9.2f} %") + + +if __name__ == "__main__": + print("=" * 78) + print(" WAHRSCHEINLICHKEITSBESCHRAENKUNGEN IM KRAFTWERKSPARK") + print("=" * 78) + print(f"Gesichert bereitzustellen: {BEDARF:.0f} MW\n") + print(f"{'Technologie':<14} {'EUR/MW':>8} {'verfuegbar':>11} {'Streuung':>9} " + f"{'Grenze':>8} {'EUR je erw. MW':>15}") + print("-" * 78) + for i, name in enumerate(TECHNIK): + print(f"{name:<14} {KOSTEN[i]:>8,.0f} {VERFUEGBAR[i]:>10.2f} " + f"{STREUUNG[i]:>8.2f} {GRENZE[i]:>7.0f} " + f"{KOSTEN[i] / VERFUEGBAR[i]:>15,.0f}") + print(f"\nVollausbau liefert im Mittel {VERFUEGBAR @ GRENZE:.0f} MW, " + f"in der Kaeltewelle {(VERFUEGBAR * EINBRUCH) @ GRENZE:.0f} MW.") + + # Zwei grosse, unabhaengige Testmengen. An ihnen wird JEDER Plan gemessen - + # einmal in der unterstellten Normalwelt, einmal in der echten Verteilung. + echt = ziehe_wetter(200_000, seed=771) + normalwelt = np.random.default_rng(4711).multivariate_normal( + VERFUEGBAR, SIGMA, size=200_000) + + print("\n" + "=" * 78) + print(" (1) Mittelwertplan und Chance Constraints im Vergleich") + print("=" * 78) + print(KOPF) + print("-" * 78) + + k_mittel, x_mittel = mittelwertplan() + stufen = [("Mittelwert", k_mittel, sicherheit(x_mittel, normalwelt))] + print(zeile("Mittelwert", k_mittel, x_mittel, + sicherheit(x_mittel, normalwelt), sicherheit(x_mittel, echt))) + + plaene = {} + for alpha in (0.20, 0.10, 0.05, 0.01): + kosten, x = chance_constraint_analytisch(alpha) + plaene[alpha] = (kosten, x) + quote_normal = sicherheit(x, normalwelt) + stufen.append((f"Zusage {(1 - alpha) * 100:.0f} %", kosten, quote_normal)) + print(zeile(f"Zusage {(1 - alpha) * 100:.0f} %", kosten, x, + quote_normal, sicherheit(x, echt))) + + print("\n In der Normalwelt trifft jede Zusage ihren Wert - das Verfahren") + print(" rechnet richtig. Die Spalte 'echte Welt' kommt in Teil (3).") + + print("\n" + "=" * 78) + print(" (2) Was kostet ein Prozentpunkt Versorgungssicherheit?") + print("=" * 78) + print(f"{'von -> nach':<26} {'Prozentpunkte':>13} {'Mehrkosten':>13} " + f"{'EUR je Punkt':>13}") + print("-" * 78) + for (n1, k1, q1), (n2, k2, q2) in zip(stufen, stufen[1:]): + d_punkte = (q2 - q1) * 100 + d_kosten = k2 - k1 + print(f"{n1 + ' -> ' + n2:<26} {d_punkte:>13.2f} {d_kosten:>13,.0f} " + f"{d_kosten / d_punkte:>13,.0f}") + erst = (stufen[1][1] - stufen[0][1]) / ((stufen[1][2] - stufen[0][2]) * 100) + letzt = (stufen[-1][1] - stufen[-2][1]) / ((stufen[-1][2] - stufen[-2][2]) * 100) + print(f"\n Der letzte Prozentpunkt kostet das {letzt / erst:.1f}-fache des ersten.") + print(" Sicherheit ist konvex bepreist - genau deshalb muss jemand") + print(" entscheiden, wie viel davon das Unternehmen kaufen will.") + + print("\n" + "=" * 78) + print(" (3) Und wenn die Verteilung nicht normal ist?") + print("=" * 78) + k_soc, x_soc = plaene[0.05] + print(f"Die echte Welt kennt die Kaeltewelle mit Dunkelflaute: in " + f"{P_KAELTEWELLE * 100:.0f} % der Faelle liefern") + print(f"Wind {EINBRUCH[1] * 100:.0f} %, Sonne {EINBRUCH[2] * 100:.0f} % " + f"und - entscheidend - auch das Gaskraftwerk nur " + f"{EINBRUCH[0] * 100:.0f} %") + print("des Ueblichen. Alles bricht gleichzeitig ein.\n") + + a_bau = ziehe_wetter(400, seed=20260908) + k_sz, x_sz = chance_constraint_szenarien(a_bau, 0.05) + + print(KOPF) + print("-" * 78) + print(zeile("SOC, Zusage 95 %", k_soc, x_soc, + sicherheit(x_soc, normalwelt), sicherheit(x_soc, echt))) + print(zeile("Szenarien, 95 %", k_sz, x_sz, + sicherheit(x_sz, normalwelt), sicherheit(x_sz, echt))) + + print(f"\n Der SOC-Plan verspricht 95 % und haelt " + f"{sicherheit(x_soc, echt) * 100:.1f} %. Nicht das Verfahren ist") + print(f" falsch, sondern die Annahme: In der Normalwelt liefert derselbe") + print(f" Plan {sicherheit(x_soc, normalwelt) * 100:.1f} %.") + print(f"\n Der Szenarioplan ({len(a_bau)} Szenarien, {len(a_bau)} " + f"Binaervariablen) kommt auf") + print(f" {sicherheit(x_sz, echt) * 100:.1f} % - und kostet dafuer " + f"{(k_sz / k_soc - 1) * 100:+.1f} %. Er kauft keine Windkraft mehr:") + print(" Was im Ernstfall ausfaellt, hilft der Zusage nicht.") + print("=" * 78) diff --git a/Operations_Research_mit_Python_Version_04_Programme/README.md b/Operations_Research_mit_Python_Version_04_Programme/README.md index 67ae8bc..a47a71c 100644 --- a/Operations_Research_mit_Python_Version_04_Programme/README.md +++ b/Operations_Research_mit_Python_Version_04_Programme/README.md @@ -1,6 +1,6 @@ # Beispielprogramme zu „Optimierte Entscheidungsfindung mit Python" (Version 04) -Diese 73 Dateien sind automatisch aus den Kapiteldateien in +Diese 74 Dateien sind automatisch aus den Kapiteldateien in `../Operations_Research_mit_Python_Version_04/` extrahiert — jede Datei entspricht genau einem im Buch vollständig abgedruckten Programm. Sie sind hier zusätzlich als eigenständige, direkt ausführbare `.py`-Dateien verfügbar, damit man sie ohne @@ -15,7 +15,7 @@ python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py ``` Das Verzeichnis „Verzeichnis der Beispielprogramme" im Vorwort des Buchs -(`00_Vorwort_und_Lesehilfe.md`) listet dieselben 73 Programme mit Kurzbeschreibung. +(`00_Vorwort_und_Lesehilfe.md`) listet dieselben 74 Programme mit Kurzbeschreibung. Auch dieses README ist selbst generiert: `extract_programme.py` erzeugt es bei jedem Lauf aus der Vorlage `README_Programme.md` im Quellverzeichnis und setzt die diff --git a/PROGRESS.md b/PROGRESS.md index 8cacb7d..2699eee 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -77,14 +77,14 @@ sie unerwartet ab, ist etwas kaputtgegangen. | Kapiteldateien | 22 | **31** | | Kapitel | 15 | **23** | | Anhänge | 4 | **5** | -| Zeilen im Gesamtdokument | 11 082 | **27 015** | -| Größe des Gesamtdokuments | 606 KB | **1 512 KB** | -| Hauptüberschriften | 131 | **276** | -| registrierte Abschnitte | 122 | **292** | -| aufgelöste Querverweise | 314 | **706** (0 unaufgelöst) | -| Indexmarken | 295 | **325** | -| Beispielprogramme | 41 | **73** (alle lauffähig) | -| PDF-Seiten | — | **715** | +| Zeilen im Gesamtdokument | 11 082 | **27 400** | +| Größe des Gesamtdokuments | 606 KB | **1 536 KB** | +| Hauptüberschriften | 131 | **289** | +| registrierte Abschnitte | 122 | **293** | +| aufgelöste Querverweise | 314 | **712** (0 unaufgelöst) | +| Indexmarken | 295 | **327** | +| Beispielprogramme | 41 | **74** (alle lauffähig) | +| PDF-Seiten | — | **725** | | Notebooks | — | **25** | | Plotly-Figuren | — | **4** | | Diagramme (SVG) | 26 | **32**, davon **18** mit Generatorskript (15 Skripte) | @@ -1329,18 +1329,25 @@ pytest-Tests grün, Version 03 unberührt. ### Der konkret nächste Handgriff -**Alle sechs Phasen sind abgeschlossen, und Version 04 liegt getrennt in `Version_04/`.** -23 Kapitel, 5 Anhänge, 73 lauffähige Programme, 715 PDF-Seiten, Website mit vier -interaktiven Figuren. +**Alle sechs Phasen sind abgeschlossen. Seit dem 08.09.2026 ist `Version_04/` ein eigenes +Git-Repository**, und Phase 6 (Umsetzung von `Verbesserungen_02/_03`) hat begonnen. +23 Kapitel, 5 Anhänge, 74 lauffähige Programme, Website mit vier interaktiven Figuren. -Offen ist nur noch, was von außen kommt: +**Zum eigenen Repository — eine Korrektur der früheren Angabe hier:** `git subtree split -P +Version_04` hätte die Historie **nicht** mitgenommen. Der Pfad `Version_04/` existiert erst +seit der Verzeichnistrennung; der Split hätte 7 statt 41 einschlägiger Commits gebracht — eine +Teilhistorie, die vollständig aussieht und es nicht ist. Volle Historie ginge nur über +`git-filter-repo` (nicht installiert). Deshalb: frischer `git init -b main`, das +übergeordnete Repository bleibt das Archiv der 71 Commits und trägt `Version_04/` in seiner +`.gitignore`. + +Offen ist weiterhin, was von außen kommt: * **Veröffentlichen.** `Version_04/OR_HTML_04/` ist selbstgenügsam und direkt auf einen Webserver kopierbar. -* **Eigenes Repository.** `git subtree split -P Version_04 -b v04-only` nimmt die Historie - mit. Danach **`COLAB_BASIS_URL`** in `build_version_04.py` auf Kontoname und - Repository-Name setzen — die 25 Colab-Badges hängen daran, und ein falscher Wert fällt - erst beim Klicken auf. +* **Remote-Repository anlegen.** Danach **`COLAB_BASIS_URL`** in `build_version_04.py` auf + Kontoname und Repository-Name setzen — die 25 Colab-Badges hängen daran, und ein falscher + Wert fällt erst beim Klicken auf. Was einer späteren Sitzung als Erstes auffallen wird und **kein Fehler** ist: @@ -1363,6 +1370,106 @@ Was einer späteren Sitzung als Erstes auffallen wird und **kein Fehler** ist: --- +## 6d. Phase 6 — Umsetzung von `Verbesserungen_02` / `_03` 🔄 + +Grundlage: `Kritik_und_Verbesserungsvorschlaege/Verbesserungen_02.md` und `_03.md`, zwölf +Arbeitspakete. Nach Ertrag geordnet umgesetzt; **Streamlit-Apps sind auf Wunsch des Autors +zurückgestellt**, ebenso Folium-Karten (die VRP-Instanz ist ein synthetisches Raster ohne +Geokoordinaten) und Zielgruppen-Callouts in allen Kapiteln. + +### ✅ Repository und `pyproject.toml` + +`git init -b main` in `Version_04`, `Version_04` im übergeordneten Repository ausgetragen. +Neu: `pyproject.toml` mit den Gruppen `finance`, `large-scale`, `api`, `figures`, `dev`, +`empfehlungen` — die abgedruckte `requirements.txt` bleibt unverändert daneben bestehen. + +**`ortools` steht in der Grundausstattung, `highspy` erst in `[large-scale]`.** Damit kann +der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten; `cvxpy` +zieht `highspy` nicht nach. + +Zwei Funde beim Abgleich der Importe gegen `requirements.txt`: + +* **`graphviz` fehlte** — `bilder_04/erzeuge_architektur_diagramme.py` importiert es, die + Leserliste kennt es nicht. Es gehört nicht in die abgedruckte Datei (kein + Beispielprogramm braucht es), steht jetzt aber in `[figures]`. +* **`pymoo` steht in `requirements.txt`, wird aber von keinem der 74 Programme importiert** — + es ist nur eine Empfehlung im Kapitel Metaheuristiken. Jetzt in `[empfehlungen]`. + +Später korrigiert: **`cvxpy` lag zuerst in `[finance]`** — falsch, es wird von 13 Programmen +gebraucht, darunter dreien im Kapitel Unsicherheit, lange vor dem Finanzteil. Jetzt in der +Grundausstattung. + +### ✅ 6.1 Chance Constraints ({ref:kap:unsicherheit}) + +Neuer Abschnitt {ref:sec:unsicherheit-chance-constraints} plus `Chance_Constraints.py` +(74. Programm). Das Kapitel hatte Monte-Carlo, Zweistufigkeit und Worst-Case-Robustheit; die +Wahrscheinlichkeitszusage war die fehlende vierte Antwort — und die, nach der das Management +tatsächlich fragt. Beide Wege an **derselben** Instanz: analytisch als +Second-Order-Cone-Bedingung (CLARABEL) und szenariobasiert als Big-M-MILP (SciPy/HiGHS), +beide in einem Prozess ohne `ortools`- oder `highspy`-Import. + +Der Fall ist ein Kraftwerkspark mit 500 MW Zusage. **Drei Befunde, alle gemessen:** + +1. **Der Mittelwertplan hält 50,08 %.** Kein Fehler, sondern die Definition des + Erwartungswerts — dieselbe Falle wie im Kapitelanfang, nur teurer. +2. **Sicherheit ist konvex bepreist.** 56 289 € je Prozentpunkt auf dem Weg zu 80 %, + 253 848 € zwischen 95 % und 99 % — das **4,5-fache**. Das Programm rechnet die Tabelle + selbst aus, statt sie zu behaupten. +3. **Die Zusage gilt nur für die unterstellte Verteilung.** Der 95-%-Plan hält gemessen + **87,44 %**, sobald die Testverteilung eine Kältewelle mit Dunkelflaute enthält, in der + *alles zugleich* einbricht — auch das Gaskraftwerk. In einer reinen Normalwelt liefert + derselbe Plan 94,97 %. Nicht das Verfahren ist falsch, sondern die Annahme. + +**Zwei eigene Fehlgriffe unterwegs, beide durch Messen aufgefallen:** + +* Die erste Kostentarierung ergab eine **entartete Lösung** (alles ins Gaskraftwerk), womit + die Kovarianzmatrix wirkungslos war — und gerade sie ist der Grund für die Kegelform. Erst + Kosten, die je *erwarteter* MW vergleichbar sind, machen den Konflikt echt. +* Der erste Kälteeinbruch traf nur Wind und Sonne. Der SOC-Plan hatte die aber ohnehin fast + herausgehalten und war zufällig robust (94,40 %) — das Argument trug nicht. Physikalisch + richtig ist, dass bei Frost auch der Gasdruck fällt; damit trifft der Einbruch alles + zugleich, und genau das kann eine Kovarianzmatrix mit Korrelationen um null nicht + ausdrücken. + +**Ehrlich berichtet statt geglättet:** Die Szenariomethode **überanpasst**. Über zwölf Läufe +(je vier Ziehungen bei S = 200, 400, 800, geprüft an 200 000 unabhängigen Szenarien) lag die +tatsächliche Quote zwischen **93,14 % und 96,71 %**, und die Spanne wurde von S = 400 auf +S = 800 wieder **breiter**. Beide Solver (SciPy und HiGHS) bestätigten `optimal` bei +identischen Kosten — es ist echte Überanpassung, kein Solverartefakt. Steht als +⚠️-Kasten im Abschnitt. + +**Aus der Ausgabe entfernt: die Laufzeitmessung.** Eine Wanduhrzeit ist nie byteidentisch +reproduzierbar (2,2 s / 2,3 s zwischen zwei Läufen) und hätte Regel 4 dauerhaft gebrochen. +Die Beobachtung steht jetzt im Fließtext, wo eine Spanne ehrlich ist. Danach: drei Läufe +zeichengleich, und die abgedruckte Ausgabe stimmt mit dem Lauf des **extrahierten** +Programms überein. + +Eine Aufgabenprämisse musste ich verwerfen: „Ab welcher Zusage wird das Modell unlösbar?“ — +das analytische Modell bleibt bis praktisch 100 % lösbar. Die Aufgabe fragt jetzt nach dem +Fall mit gedrosselter Biomasse, in dem das **szenariobasierte** Modell `infeasible` meldet +(7,50 % der Szenarien deckt selbst der Vollausbau nicht), das analytische aber `optimal` +liefert — und dann 87,39 % hält. + +### ⏳ Offen: die Lösungsnummern in Anhang A — **sechste** harte Nummer + +**Gefunden, noch nicht behoben.** Der Lösungsanhang trägt **97** feste Marken der Bauart +`**9.2 — Ansatz wählen.**`. Sie stammen aus Version 03, wo Unsicherheit Kapitel 9 war. Heute +ist es Kapitel 12, und die Aufgabe selbst wird korrekt als **„Aufgabe 12.2"** gesetzt. + +Das ist keine Schönheitsfrage: **„Aufgabe 9.2" existiert heute wirklich** — sie gehört zum +Kapitel Metaheuristiken („Zuggröße und Temperatur"). Wer die Lösung zu 9.2 sucht, landet +beim falschen Thema. + +Beide Prüfungen laufen daran vorbei: Vor der Zahl steht kein Schlüsselwort, und es ist keine +Tabellenzelle. + +**Vorschlag zur Behebung** (eigenes Arbeitspaket, rührt an 97 Stellen plus Build): +Die Zuordnung ist herleitbar — der Abschnitt `{#sec:loesungen-}` gehört zum Kapitel +`{#kap:}`. Der Build kann die Marken also selbst vergeben, so wie er es für Aufgaben +bereits tut; im Quelltext bliebe nur noch der Titel stehen. + +--- + ## 8. Commit-Historie des V04-Strangs > **Zu den Kapitelnummern in dieser Tabelle:** Sie stammen aus den Commit-Betreffs und geben diff --git a/README.md b/README.md index 87ba0ad..7fc9beb 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ daraus ab, und alle Befehle unten werden **hier** ausgeführt. | `Operations_Research_mit_Python_Version_04/` | **Quelle**: 31 Kapiteldateien + Build-Skripte | | `bilder_04/` | **Quelle**: Diagramme (SVG/PNG) + `erzeuge_*.py`-Generatoren | | `Operations_Research_mit_Python_Version_04.md` | generiert: Gesamtdokument (Pandoc-Eingabe) | -| `Operations_Research_mit_Python_Version_04.pdf` | generiert: PDF, 715 Seiten | +| `Operations_Research_mit_Python_Version_04.pdf` | generiert: PDF, 725 Seiten | | `OR_HTML_04/` | generiert: **Mehrseiten-Website** — dieser Ordner wird veröffentlicht | -| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 73 lauffähige Beispielprogramme | +| `Operations_Research_mit_Python_Version_04_Programme/` | generiert: 74 lauffähige Beispielprogramme | | `Notebooks_04/` | generiert: ein Jupyter-Notebook je Kapitel | | `PLAN.md` / `PROGRESS.md` | das Ziel des Umbaus und der erreichte Stand | | `pyproject.toml` | Abhängigkeiten in Gruppen (`finance`, `large-scale`, `api`, …) | @@ -66,8 +66,8 @@ Wer eine schlanke Umgebung möchte, nimmt stattdessen [`pyproject.toml`](pyproje seinen Gruppen — die Grundausstattung trägt den Großteil des Buchs: ```bash -pip install -e . # numpy, scipy, pandas, matplotlib, openpyxl, pydantic, ortools -pip install -e ".[finance]" # cvxpy, scikit-learn, yfinance +pip install -e . # numpy, scipy, pandas, matplotlib, openpyxl, pydantic, ortools, cvxpy +pip install -e ".[finance]" # scikit-learn, yfinance pip install -e ".[large-scale]" # highspy, Pyomo, linopy, polars pip install -e ".[api]" # fastapi, uvicorn, httpx pip install -e ".[figures]" # plotly, graphviz — nur zum Neuerzeugen der Diagramme @@ -76,8 +76,9 @@ pip install -e ".[dev]" # pytest **`ortools` steht in der Grundausstattung, `highspy` erst in `[large-scale]`.** Das ist Absicht: Beide bringen eine eigene HiGHS-Kopie mit und vertragen sich nicht im selben -Prozess. Wer nur `pip install -e .` nutzt, kann in den Konflikt gar nicht geraten; `cvxpy` -aus `[finance]` zieht ein installiertes `highspy` bei der Solver-Erkennung selbst mit herein. +Prozess. Wer die Grundausstattung installiert, kann in den Konflikt gar nicht geraten — +`cvxpy` zieht `highspy` **nicht** nach, erkennt es aber, sobald `[large-scale]` es +mitgebracht hat. `graphviz` fehlt in der `requirements.txt` — es wird allein von `bilder_04/erzeuge_architektur_diagramme.py` gebraucht, nicht von den Beispielprogrammen, diff --git a/pyproject.toml b/pyproject.toml index 630e1fe..89f36f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,12 +50,16 @@ dependencies = [ "openpyxl>=3.1.5", # kein direkter Import: Excel-Motor hinter pandas "pydantic>=2.12", "ortools>=9.15.6755", + # cvxpy gehoert in die Grundausstattung, nicht in [finance]: Es wird von + # 13 Programmen gebraucht, darunter dreien im Kapitel Unsicherheit, lange + # vor dem Finanzteil. Es zieht KEIN highspy nach - der Solverkonflikt + # entsteht erst mit [large-scale]. + "cvxpy>=1.9.2", ] [project.optional-dependencies] # Teil "Anwendungen - Energiewirtschaft und Finanzmaerkte" finance = [ - "cvxpy>=1.9.2", "scikit-learn>=1.8.0", "yfinance>=1.2.0", ]