operations_research/OR_HTML_04/modellierung.html

1016 lines
112 KiB
HTML
Raw Normal View History

Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Kapitel 4: Vom Management-Wunsch zum Modell · Optimierte Entscheidungsfindung mit Python</title>
<script>
(function () {
try {
var t = localStorage.getItem("or-theme");
if (t) document.documentElement.setAttribute("data-theme", t);
} catch (e) {}
})();
</script>
<link rel="stylesheet" href="assets/highlight.css" />
<link rel="stylesheet" href="katex/katex.min.css" />
<script defer="" src="katex/katex.min.js"></script>
<script>document.addEventListener("DOMContentLoaded", function () {
var mathElements = document.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains('display'),
throwOnError: false,
macros: macros,
fleqn: false
});
}}
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
if (location.hash) {
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
}
});
</script>
<link rel="stylesheet" href="assets/site.css" />
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
</symbol>
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="4.5"/>
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
</symbol>
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
</symbol>
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 4 7 12 15 20"/>
</symbol>
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 4 17 12 9 20"/>
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 13 9.5 18.5 20 6"/>
</symbol>
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
</symbol>
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
</symbol>
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</symbol>
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
</symbol></svg>
<header class="site-header">
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
<div class="site-search">
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
</div>
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
</button>
</header>
<div class="site-body">
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe" open><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html" class="aktiv"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><li data-kapitel="synthese-grundlagen.html"><a href="synthese-grundlagen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><li data-kapitel="synthese-kernverfahren.html"><a href="synthese-kernverfahren.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<main class="content">
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> <span>Teil I</span> <span>Kapitel 4: Vom Management-Wunsch zum Modell</span></nav>
Phase 8.1: Synthese-Seiten je Teil - und die gebrochene Lesekette Setzt Paket 5 aus Verbesserungen_02.md um (den Teil, der nicht zurueckgestellt wurde). Fuenf neue Dateien 19_/29_/39_/49_/52_Synthese_*.md, je eine am Ende eines Teils, mit eigener Website-Seite ueber SONDERSEITEN - sie tragen bewusst keine "# Kapitel:"-Ueberschrift, weil sie keine Kapitel sind, sondern der Rueckblick auf einen Teil. Der Entwurf musste sich abgrenzen: Die Teil-Einleitungen haben bereits Entscheidungsdiagramme. Eine zweite Matrix am Teil-Ende waere eine Dopplung gewesen. Die Synthesen leisten deshalb, was eine Einleitung nicht kann - den Vergleich ueber die Kapitel hinweg (Verfahren nebeneinander, mit der Spalte "wo es aufhoert"), eine Tabelle "was dieser Teil gemessen hat" (Behauptung gegen Messung gegen Fundstelle) und drei Fehler, die der Teil verhindert. Zitiert wird ausschliesslich, was im Buch tatsaechlich gerechnet wird. Drei Funde beim Einbau: * Teil III sagte "die drei Kapitel dieses Teils", hat aber fuenf. Phase 3 hatte Mehrziel und Predict-then-Optimize hinzugefuegt, die Einleitung blieb stehen. * 50_Praxis.md verwies auf die Projektwerkstatt mit "acht eigene Anwendungen" - sie hat elf. * Und der eigentliche Fund: Die Lesekette der Quelldateien fuehrte an ACHT Kapiteln vorbei. 12_Python_Oekosystem zeigte direkt auf 20_Lineare_Programmierung, 23_Graphen direkt auf 30_QP, 32_Dynamische direkt auf 40_Finanzdaten, 50_Praxis direkt auf die Projektwerkstatt. Wer der Kette folgte, uebersprang acht von 23 Kapiteln - darunter Metaheuristiken, Spaltengenerierung, Strukturbruecke, Supply-Chain und das ganze Testing-Kapitel. Zehn weitere Dateien hatten gar keine Navigationszeile. Zur Reichweite, damit sie nicht ueberschaetzt wird: Diese Zeilen stehen nur in den Quelldateien. entferne_navigation() streicht sie aus dem Gesamtdokument, und die Website baut ihre Vor/Zurueck-Knoepfe selbst aus DATEIEN. PDF und Website waren nie betroffen - wohl aber jeder, der die Markdown-Dateien im Repository liest, und das wird nach der Veroeffentlichung der Normalfall sein. Die Kette ist jetzt ueber alle 35 Uebergaenge geschlossen, und --check bewacht sie: Fehlt eine Zeile oder zeigt sie an der in DATEIEN folgenden Datei vorbei, ist der Lauf rot. Gegengetestet mit beiden Bruchformen. Stand: 36 Dateien, 296 Abschnitte, 815 Querverweise, 328 Indexmarken, 76 Programme (unveraendert), 33 pytest-Tests, PDF 758 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 12:10:02 +02:00
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="oekosystem.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a><a class="prev-next-knopf prev-next-next" href="synthese-grundlagen.html"><span><small>Weiter</small>Synthese Teil I</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<article>
<h1 id="kap-modellierung">Kapitel 4: Vom Management-Wunsch zum Modell</h1>
<div class="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.</p>
<p><strong>Voraussetzungen:</strong> <a href="einfuehrung.html#kap-einfuehrung">Kapitel 1</a> (die vier Bausteine eines Modells). Kein Solverwissen nötig; das ganze Kapitel kommt mit einer einzigen <code>linprog</code>-Zeile aus.</p>
<p><strong>Danach können Sie:</strong> eine Anforderung daraufhin abklopfen, was sie <em>nicht</em> sagt; die Zielgröße so wählen, dass sie keinen Fehlanreiz setzt; harte von weichen Regeln unterscheiden — und begründen, warum diese Unterscheidung ein Jahr später über die Betriebsfähigkeit entscheidet.</p>
<p><strong>Zeitbedarf:</strong> ca. 3 Stunden.</p>
<p><strong>Programme:</strong><br />
<code>Vom_Wunsch_zum_Modell.py</code></p>
Veroeffentlichung: Lizenzen, README, Colab-Entscheidung, JupyterLab im Image Sechs Dinge, alle fuer das jetzt oeffentliche Repository. LIZENZEN. LICENSE traegt die MIT-Lizenz fuer den Programmcode (alle .py, Notebooks, Dockerfile, pyproject.toml), LICENSE-TEXT.md die CC BY-SA 4.0 fuer Buchtext, PDF, Website und Diagramme. Beide Dateien nennen ausdruecklich, was sie abdecken und was nicht - ein Codeblock im Buchtext bleibt Programmcode und steht unter MIT. Vorher hatte das Repository gar keine Lizenz, womit standardmaessig "alle Rechte vorbehalten" galt und niemand die 76 Programme haette weiterverwenden duerfen. COLAB. COLAB_BASIS_URL steht jetzt auf "". Der Platzhalter zeigte auf GitHub, das Repository liegt auf einer eigenen Forgejo-Instanz - und Colab oeffnet Notebooks NUR aus GitHub, Google Drive oder einem Upload. Die URL-Form colab.research.google.com/github/... ist fest auf GitHub verdrahtet; eine selbstgehostete Adresse dort einzusetzen ergaebe keinen Link zum eigenen Server, sondern einen toten GitHub-Link. Der Kommentar im Quelltext ging von GitHub aus und war damit selbst irrefuehrend; er ist ersetzt. Verloren geht nichts: Die 25 Notebooks liegen neben der Website und bekommen einen echten Download-Link, jetzt mit dem Hinweis, was man damit tut - "herunterladen und in Jupyter oeffnen, in Colab hochladen oder mit dem Kurs-Image starten". JUPYTERLAB IM KURS-IMAGE. Neue pyproject-Gruppe [notebook] mit jupyterlab, die Notebooks werden ins Image kopiert, und ein kleiner Startbefehl macht beide Betriebsarten ohne --entrypoint moeglich: ohne Argument JupyterLab, mit Argument ein einzelnes Programm. Gebaut und geprueft - Rucksack.py laeuft, JupyterLab antwortet mit HTTP 200 und zeigt alle 25 Notebooks. Image 1,31 -> 1,46 GB. Es laeuft ohne Token, deshalb im README die Portfreigabe an 127.0.0.1 gebunden. README KOMPLETT NEU. Es war die Bau-Anleitung eines privaten Verzeichnisses und ist jetzt die Visitenkarte eines oeffentlichen Repositorys: was das Buch ist, wo man es liest, drei Wege die Beispiele auszufuehren (Container, schlanke Installation, alles auf einmal), was hier liegt, wie man baut, die Colab-Frage, die Lizenzen und wie man mitwirkt. Alle relativen Links geprueft: 0 tot. .gitattributes. Ohne die Datei entschied core.autocrlf des jeweiligen Rechners, was beim Klonen passiert - ein Windows-Leser bekam CRLF-Rauschen in jedem Diff. Jetzt: im Repository immer LF, im Arbeitsverzeichnis passend zum System, Binaerdateien ausdruecklich ausgenommen. Der Bestand war bereits sauber (git add --renormalize aendert null Dateien). Zusaetzlich sind die erzeugten Verzeichnisse als linguist-generated markiert, sonst zaehlt die Sprachstatistik das Repository als HTML-Projekt. PROGRESS.md: Remote-Repository als erledigt markiert, der Colab-Befund festgehalten. CLAUDE.md um Veroeffentlichung, Lizenzen und die neuen Dateien ergaenzt. Geprueft: --check ohne Fehler, 0 tote README-Links, 76 Programme unveraendert, 33 pytest-Tests, pyproject baut mit acht Gruppen, PDF 759 Seiten (eine weniger - die Colab-Zeile entfaellt in 25 Kapiteln). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 14:54:04 +02:00
<p><strong>Notebook:</strong> <a href="Notebooks_04/modellierung.ipynb">modellierung.ipynb</a> — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten</p>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
</blockquote>
</div>
<hr />
<h2 id="sec:modellierung-schnellstart">4.1 In 5 Minuten gelöst</h2>
<div class="card card-schnellstart">
<blockquote>
<p><strong>🚀 In 5 Minuten gelöst: „Maximieren Sie den Umsatz”</strong></p>
<p>Eine Lohnfertigung hat fünf Anfragen und 60 freie Maschinenstunden. Die Anfragen verlangen zusammen 160 Stunden — es muss ausgewählt werden.</p>
<table>
<thead>
<tr class="header">
<th>Auftrag</th>
<th style="text-align: right;">Stunden</th>
<th style="text-align: right;">Umsatz</th>
<th style="text-align: right;">Material</th>
<th style="text-align: right;">Deckungsbeitrag</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>A</strong></td>
<td style="text-align: right;">60</td>
<td style="text-align: right;">60 000 €</td>
<td style="text-align: right;">55 000 €</td>
<td style="text-align: right;">5 000 €</td>
</tr>
<tr class="even">
<td><strong>B</strong></td>
<td style="text-align: right;">30</td>
<td style="text-align: right;">22 000 €</td>
<td style="text-align: right;">12 000 €</td>
<td style="text-align: right;">10 000 €</td>
</tr>
<tr class="odd">
<td><strong>C</strong></td>
<td style="text-align: right;">30</td>
<td style="text-align: right;">20 000 €</td>
<td style="text-align: right;">11 000 €</td>
<td style="text-align: right;">9 000 €</td>
</tr>
<tr class="even">
<td><strong>D</strong></td>
<td style="text-align: right;">25</td>
<td style="text-align: right;">18 000 €</td>
<td style="text-align: right;">9 000 €</td>
<td style="text-align: right;">9 000 €</td>
</tr>
<tr class="odd">
<td><strong>E</strong></td>
<td style="text-align: right;">15</td>
<td style="text-align: right;">12 000 €</td>
<td style="text-align: right;">7 000 €</td>
<td style="text-align: right;">5 000 €</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Auftrag: A B C D E</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>stunden <span class="op">=</span> np.array([ <span class="dv">60</span>, <span class="dv">30</span>, <span class="dv">30</span>, <span class="dv">25</span>, <span class="dv">15</span>], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>umsatz <span class="op">=</span> np.array([ <span class="dv">60_000</span>, <span class="dv">22_000</span>, <span class="dv">20_000</span>, <span class="dv">18_000</span>, <span class="dv">12_000</span>], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>material <span class="op">=</span> np.array([ <span class="dv">55_000</span>, <span class="dv">12_000</span>, <span class="dv">11_000</span>, <span class="dv">9_000</span>, <span class="dv">7_000</span>], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>KAPAZITAET <span class="op">=</span> <span class="dv">60</span> <span class="co"># Maschinenstunden; die Anfragen verlangen 160</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>waehle <span class="op">=</span> <span class="kw">lambda</span> ziel: np.<span class="bu">round</span>(linprog(</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span>ziel, A_ub<span class="op">=</span>[stunden], b_ub<span class="op">=</span>[KAPAZITAET],</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="dv">1</span>), integrality<span class="op">=</span><span class="dv">1</span>, method<span class="op">=</span><span class="st">&quot;highs&quot;</span>).x).astype(<span class="bu">int</span>)</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> wunsch, ziel <span class="kw">in</span> [(<span class="st">&quot;Umsatz maximieren&quot;</span>, umsatz),</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Deckungsbeitrag maximieren&quot;</span>, umsatz <span class="op">-</span> material)]:</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> plan <span class="op">=</span> waehle(ziel)</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>wunsch<span class="sc">:&lt;27}</span><span class="ss"> -&gt; </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">.</span>join(b <span class="cf">for</span> b, ja <span class="kw">in</span> <span class="bu">zip</span>(<span class="st">&#39;ABCDE&#39;</span>, plan) <span class="cf">if</span> ja)<span class="sc">:&lt;5}</span><span class="ss">&quot;</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot; Umsatz </span><span class="sc">{</span>umsatz <span class="op">@</span> plan<span class="sc">:&gt;7,.0f}</span><span class="ss"> DB </span><span class="sc">{</span>(umsatz <span class="op">-</span> material) <span class="op">@</span> plan<span class="sc">:&gt;6,.0f}</span><span class="ss">&quot;</span>)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>Umsatz maximieren -&gt; A Umsatz 60,000 DB 5,000
Deckungsbeitrag maximieren -&gt; BD Umsatz 40,000 DB 19,000</code></pre>
</blockquote>
</div>
<p><strong>Und jetzt der Punkt.</strong> Beide Modelle sind korrekt. Beide Pläne sind zulässig. Beide sind <em>optimal</em> — nur für verschiedene Fragen.</p>
<p>Wer „Umsatz maximieren” sagt, bekommt Auftrag A: 60 000 € Umsatz und 5 000 € Deckungsbeitrag. Wer „Deckungsbeitrag maximieren” sagt, bekommt B und D: 20 000 € weniger Umsatz und <strong>14 000 € mehr Ergebnis</strong>. Der Umsatzplan verkauft die gesamte Kapazität für eine Marge von 8 %.</p>
<p>Niemand in der Besprechung hätte behauptet, Umsatz sei wichtiger als Ertrag. Der Satz „maximieren Sie den Umsatz” ist trotzdem gefallen, weil Umsatz die Zahl ist, die im Monatsbericht steht.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.</p>
</blockquote>
<p><strong>Warum funktioniert das?</strong> Weil eine Zielfunktion eine <strong>vollständige</strong> Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern <strong>wertlos</strong>. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.</p>
<hr />
<h2 id="sec:modellierung-lernziele">4.2 Lernziele</h2>
<p>Nach diesem Kapitel können Sie …</p>
<ol type="1">
<li>… erklären, warum die Zielgröße die folgenreichste Entscheidung eines OR-Projekts ist — folgenreicher als die Wahl des Solvers.</li>
<li>… die vier Fragen stellen, die aus einer Anforderung ein Modell machen.</li>
<li>… erkennen, dass ein Satz wie „Stammkunden dürfen wir nicht verlieren” mehrere Lesarten hat, und den Unterschied in Euro beziffern.</li>
<li>… begründen, wann eine Regel hart und wann sie weich sein muss — und was eine harte Regel im Betrieb anrichtet, wenn sie einmal nicht erfüllbar ist.</li>
<li>… einen Kennzahlen-Fehlanreiz benennen, bevor er in die Zielfunktion gerät.</li>
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<li>… einen Satz aus einer Besprechung in den passenden Modellierungsbaustein übersetzen — und die fünf Wendungen erkennen, bei denen genau das nicht geht, ohne vorher nachzufragen.</li>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
</ol>
<hr />
<h2 id="sec:modellierung-vier-fragen">4.3 Ein Satz ist noch kein Modell</h2>
<p>Was aus einer Besprechung mitkommt, klingt meist so:</p>
<blockquote>
<p><em>„Wir müssen die Maschinen besser auslasten. Und die Stammkunden dürfen wir nicht verlieren.”</em></p>
</blockquote>
<p>Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable, keine messbare Zielgröße, keine Nebenbedingung. Sie enthalten nicht einmal genug, um zu erkennen, <em>was</em> entschieden werden soll.</p>
<p>Der Weg dorthin führt über vier Fragen. Sie sind unspektakulär, und genau deshalb werden sie übersprungen.</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>#</th>
<th>Frage</th>
<th>Was ohne sie passiert</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>1</td>
<td><strong>Worüber wird entschieden?</strong> Welche Größen kann der Betrieb tatsächlich festlegen?</td>
<td>Man modelliert Dinge, die gar nicht zur Disposition stehen — oder übersieht die eine Stellschraube, um die es geht.</td>
</tr>
<tr class="even">
<td>2</td>
<td><strong>Woran wird der Erfolg gemessen?</strong> Welche Zahl steht am Jahresende im Bericht?</td>
<td>Man optimiert eine Kennzahl, die niemanden interessiert — oder eine, deren Maximierung schadet.</td>
</tr>
<tr class="odd">
<td>3</td>
<td><strong>Was ist unverhandelbar, was nur unerwünscht?</strong></td>
<td>Jede unnötig harte Regel ist eine Zeitbombe: Irgendwann ist sie nicht erfüllbar, und das System antwortet gar nicht mehr.</td>
</tr>
<tr class="even">
<td>4</td>
<td><strong>Was passiert, wenn es nicht geht?</strong></td>
<td>Der Nachtlauf bricht ab, und niemand weiß, welche Regel schuld war.</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>📖 Zur zweiten Frage</strong></p>
<p>Sie ist absichtlich anders gestellt als „Was ist Ihnen wichtig?“. Auf diese Frage antwortet jeder Betrieb mit einer Liste, in der alles wichtig ist. Die brauchbare Frage lautet: <strong>Welche Zahl steht am Jahresende im Bericht, und wer wird danach beurteilt?</strong></p>
<p>Die Antwort ist unbequem und aufschlussreich — und sie erklärt, warum in der Besprechung „Umsatz” gesagt wird, obwohl „Deckungsbeitrag” gemeint ist.</p>
</blockquote>
<hr />
<h2 id="sec:modellierung-fuenf-modelle">4.4 Derselbe Datensatz, fünf Modelle</h2>
<p>Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14 Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300.</p>
<p>Es ist bewusst das <strong>einfachste Modell des ganzen Buchs</strong> — eine Binärvariable je Auftrag, eine Nebenbedingung. Alles, was sich zwischen den fünf Varianten ändert, ist die Frage.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Vom_Wunsch_zum_Modell.py</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Modellierung: Derselbe Datensatz, fuenf zulaessige Modelle, fuenf andere Plaene.</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">Eine Lohnfertigung kann nicht alle Anfragen annehmen: 14 Auftraege verlangen</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="co">zusammen 800 Maschinenstunden, verfuegbar sind 300. Es muss also ausgewaehlt</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">werden. Die Daten stehen fest - was FEHLT, ist die Frage.</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="co">Das Programm zeigt in vier Teilen, was daran haengt:</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="co"> 1. Drei naheliegende Ziele aus dem Managementgespraech (&quot;Umsatz&quot;, &quot;Auslastung&quot;,</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;Deckungsbeitrag&quot;) - und wie weit die Plaene auseinanderlaufen.</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="co"> 2. Der Satz &quot;Stammkunden duerfen wir nicht verlieren&quot; in drei Lesarten. Alle</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="co"> drei sind vertretbar, alle drei kosten unterschiedlich viel.</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="co"> 3. Was ein Strafgewicht daraus macht - und ab wann es dasselbe tut wie eine</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="co"> harte Regel.</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="co"> 4. Was die harte Lesart ein Quartal spaeter anrichtet.</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="co">Der Punkt des Kapitels: Nicht die Daten bestimmen das Modell, sondern die</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a><span class="co">Frage. Und die steht selten in der Anforderung.</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: numpy, scipy</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a><span class="co"># (Nummer, Kunde, Stammkunde?, Maschinenstunden, Umsatz, Materialkosten)</span></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a>AUFTRAEGE <span class="op">=</span> [</span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-101&quot;</span>, <span class="st">&quot;Weber GmbH&quot;</span>, <span class="va">True</span>, <span class="dv">40</span>, <span class="dv">28_000</span>, <span class="dv">19_000</span>),</span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-102&quot;</span>, <span class="st">&quot;Weber GmbH&quot;</span>, <span class="va">True</span>, <span class="dv">25</span>, <span class="dv">15_000</span>, <span class="dv">9_500</span>),</span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-103&quot;</span>, <span class="st">&quot;Kranz AG&quot;</span>, <span class="va">True</span>, <span class="dv">60</span>, <span class="dv">33_000</span>, <span class="dv">24_000</span>),</span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-104&quot;</span>, <span class="st">&quot;Kranz AG&quot;</span>, <span class="va">True</span>, <span class="dv">35</span>, <span class="dv">19_000</span>, <span class="dv">13_500</span>),</span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-105&quot;</span>, <span class="st">&quot;Obermeier KG&quot;</span>, <span class="va">True</span>, <span class="dv">20</span>, <span class="dv">12_000</span>, <span class="dv">7_000</span>),</span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-106&quot;</span>, <span class="st">&quot;Siedler &amp; Co&quot;</span>, <span class="va">False</span>, <span class="dv">120</span>, <span class="dv">84_000</span>, <span class="dv">71_000</span>),</span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-107&quot;</span>, <span class="st">&quot;Siedler &amp; Co&quot;</span>, <span class="va">False</span>, <span class="dv">110</span>, <span class="dv">74_000</span>, <span class="dv">63_000</span>),</span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-108&quot;</span>, <span class="st">&quot;Novatek&quot;</span>, <span class="va">False</span>, <span class="dv">95</span>, <span class="dv">61_000</span>, <span class="dv">52_000</span>),</span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-109&quot;</span>, <span class="st">&quot;Novatek&quot;</span>, <span class="va">False</span>, <span class="dv">30</span>, <span class="dv">31_000</span>, <span class="dv">17_000</span>),</span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-110&quot;</span>, <span class="st">&quot;Hellwig&quot;</span>, <span class="va">False</span>, <span class="dv">15</span>, <span class="dv">17_000</span>, <span class="dv">8_000</span>),</span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-111&quot;</span>, <span class="st">&quot;Hellwig&quot;</span>, <span class="va">False</span>, <span class="dv">55</span>, <span class="dv">41_000</span>, <span class="dv">30_000</span>),</span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-112&quot;</span>, <span class="st">&quot;Pfeiffer Werke&quot;</span>, <span class="va">False</span>, <span class="dv">80</span>, <span class="dv">46_000</span>, <span class="dv">39_000</span>),</span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-113&quot;</span>, <span class="st">&quot;Obermeier KG&quot;</span>, <span class="va">True</span>, <span class="dv">45</span>, <span class="dv">26_000</span>, <span class="dv">17_000</span>),</span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;A-114&quot;</span>, <span class="st">&quot;Pfeiffer Werke&quot;</span>, <span class="va">False</span>, <span class="dv">70</span>, <span class="dv">52_000</span>, <span class="dv">36_000</span>),</span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a>]</span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a>KAPAZITAET <span class="op">=</span> <span class="dv">300</span> <span class="co"># Maschinenstunden im Planungszeitraum</span></span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a>STUNDEN <span class="op">=</span> np.array([a[<span class="dv">3</span>] <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a>UMSATZ <span class="op">=</span> np.array([a[<span class="dv">4</span>] <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a>DECKUNGSBEITRAG <span class="op">=</span> np.array([a[<span class="dv">4</span>] <span class="op">-</span> a[<span class="dv">5</span>] <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a>IST_STAMM <span class="op">=</span> np.array([a[<span class="dv">2</span>] <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE])</span>
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a>STAMMKUNDEN <span class="op">=</span> <span class="bu">sorted</span>({a[<span class="dv">1</span>] <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE <span class="cf">if</span> a[<span class="dv">2</span>]})</span>
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> waehle_aus(ziel: np.ndarray, zusatz_ub<span class="op">=</span><span class="va">None</span>, zusatz_b_ub<span class="op">=</span><span class="va">None</span>,</span>
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a> zusatz_eq<span class="op">=</span><span class="va">None</span>, zusatz_b_eq<span class="op">=</span><span class="va">None</span>, auftraege<span class="op">=</span>AUFTRAEGE,</span>
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a> stunden<span class="op">=</span>STUNDEN, kapazitaet<span class="op">=</span>KAPAZITAET):</span>
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Ein Auftrag wird angenommen oder nicht - mehr Modell ist das nicht.</span></span>
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a><span class="co"> Der ganze Unterschied zwischen den fuenf Varianten unten steckt in &#39;ziel&#39;</span></span>
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a><span class="co"> und in den Zusatzbedingungen. Das Modell selbst bleibt dasselbe.</span></span>
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> linprog(</span>
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a> <span class="op">-</span>ziel,</span>
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>[stunden] <span class="op">+</span> (zusatz_ub <span class="kw">or</span> []),</span>
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a> b_ub<span class="op">=</span>[kapazitaet] <span class="op">+</span> (zusatz_b_ub <span class="kw">or</span> []),</span>
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a> A_eq<span class="op">=</span>zusatz_eq, b_eq<span class="op">=</span>zusatz_b_eq,</span>
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="dv">1</span>), integrality<span class="op">=</span><span class="dv">1</span>, method<span class="op">=</span><span class="st">&quot;highs&quot;</span>)</span>
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> ergebnis.success:</span>
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> np.<span class="bu">round</span>(ergebnis.x).astype(<span class="bu">int</span>)</span>
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kennzahlen(plan, auftraege<span class="op">=</span>AUFTRAEGE, stunden<span class="op">=</span>STUNDEN, umsatz<span class="op">=</span>UMSATZ,</span>
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true" tabindex="-1"></a> db<span class="op">=</span>DECKUNGSBEITRAG, ist_stamm<span class="op">=</span>IST_STAMM, kapazitaet<span class="op">=</span>KAPAZITAET):</span>
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Immer dieselben fuenf Zahlen - nur so sind die Varianten vergleichbar.&quot;&quot;&quot;</span></span>
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true" tabindex="-1"></a> abgelehnte_stammauftraege <span class="op">=</span> <span class="bu">int</span>(((<span class="dv">1</span> <span class="op">-</span> plan) <span class="op">&amp;</span> ist_stamm.astype(<span class="bu">int</span>)).<span class="bu">sum</span>())</span>
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true" tabindex="-1"></a> leer_ausgegangen <span class="op">=</span> <span class="bu">sum</span>(</span>
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true" tabindex="-1"></a> <span class="dv">1</span> <span class="cf">for</span> kunde <span class="kw">in</span> <span class="bu">sorted</span>({a[<span class="dv">1</span>] <span class="cf">for</span> a <span class="kw">in</span> auftraege <span class="cf">if</span> a[<span class="dv">2</span>]})</span>
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">any</span>(plan[i] <span class="cf">for</span> i, a <span class="kw">in</span> <span class="bu">enumerate</span>(auftraege) <span class="cf">if</span> a[<span class="dv">1</span>] <span class="op">==</span> kunde))</span>
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;umsatz&quot;</span>: <span class="bu">float</span>(umsatz <span class="op">@</span> plan), <span class="st">&quot;db&quot;</span>: <span class="bu">float</span>(db <span class="op">@</span> plan),</span>
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;auslastung&quot;</span>: <span class="bu">float</span>(stunden <span class="op">@</span> plan) <span class="op">/</span> kapazitaet <span class="op">*</span> <span class="dv">100</span>,</span>
<span id="cb3-86"><a href="#cb3-86" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;stamm_abgelehnt&quot;</span>: abgelehnte_stammauftraege,</span>
<span id="cb3-87"><a href="#cb3-87" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;kunden_leer&quot;</span>: leer_ausgegangen, <span class="st">&quot;angenommen&quot;</span>: <span class="bu">int</span>(plan.<span class="bu">sum</span>())}</span>
<span id="cb3-88"><a href="#cb3-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-89"><a href="#cb3-89" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-90"><a href="#cb3-90" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> zeile(name: <span class="bu">str</span>, plan) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb3-91"><a href="#cb3-91" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> plan <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-92"><a href="#cb3-92" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>name<span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;UNZULAESSIG - kein Plan moeglich&#39;</span><span class="sc">:&gt;44}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-93"><a href="#cb3-93" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span></span>
<span id="cb3-94"><a href="#cb3-94" aria-hidden="true" tabindex="-1"></a> k <span class="op">=</span> kennzahlen(plan)</span>
<span id="cb3-95"><a href="#cb3-95" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>name<span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>k[<span class="st">&#39;umsatz&#39;</span>]<span class="sc">:&gt;10,.0f}</span><span class="ss"> </span><span class="sc">{</span>k[<span class="st">&#39;db&#39;</span>]<span class="sc">:&gt;9,.0f}</span><span class="ss"> &quot;</span></span>
<span id="cb3-96"><a href="#cb3-96" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>k[<span class="st">&#39;auslastung&#39;</span>]<span class="sc">:&gt;6.0f}</span><span class="ss">% </span><span class="sc">{</span>k[<span class="st">&#39;stamm_abgelehnt&#39;</span>]<span class="sc">:&gt;7}</span><span class="ss"> &quot;</span></span>
<span id="cb3-97"><a href="#cb3-97" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>k[<span class="st">&#39;kunden_leer&#39;</span>]<span class="sc">:&gt;6}</span><span class="ss"> </span><span class="sc">{</span>k[<span class="st">&#39;angenommen&#39;</span>]<span class="sc">:&gt;7}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-98"><a href="#cb3-98" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-99"><a href="#cb3-99" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-100"><a href="#cb3-100" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kopfzeile() <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb3-101"><a href="#cb3-101" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Umsatz&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;DB&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Aus-&#39;</span><span class="sc">:&gt;7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Stamm-&#39;</span><span class="sc">:&gt;7}</span><span class="ss"> &quot;</span></span>
<span id="cb3-102"><a href="#cb3-102" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Kunden&#39;</span><span class="sc">:&gt;6}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;ange-&#39;</span><span class="sc">:&gt;7}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-103"><a href="#cb3-103" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;lastung&#39;</span><span class="sc">:&gt;7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;auftr.&#39;</span><span class="sc">:&gt;7}</span><span class="ss"> &quot;</span></span>
<span id="cb3-104"><a href="#cb3-104" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;ohne&#39;</span><span class="sc">:&gt;6}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;nommen&#39;</span><span class="sc">:&gt;7}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-105"><a href="#cb3-105" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; &quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb3-106"><a href="#cb3-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-107"><a href="#cb3-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-108"><a href="#cb3-108" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb3-109"><a href="#cb3-109" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-110"><a href="#cb3-110" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; VOM MANAGEMENT-WUNSCH ZUM MODELL&quot;</span>)</span>
<span id="cb3-111"><a href="#cb3-111" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-112"><a href="#cb3-112" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">len</span>(AUFTRAEGE)<span class="sc">}</span><span class="ss"> Anfragen verlangen </span><span class="sc">{</span>STUNDEN<span class="sc">.</span><span class="bu">sum</span>()<span class="sc">:.0f}</span><span class="ss"> Maschinenstunden, &quot;</span></span>
<span id="cb3-113"><a href="#cb3-113" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;verfuegbar sind </span><span class="sc">{</span>KAPAZITAET<span class="sc">}</span><span class="ss">.&quot;</span>)</span>
<span id="cb3-114"><a href="#cb3-114" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Es muss also ausgewaehlt werden. </span><span class="sc">{</span><span class="bu">len</span>(STAMMKUNDEN)<span class="sc">}</span><span class="ss"> der Kunden sind &quot;</span></span>
<span id="cb3-115"><a href="#cb3-115" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Stammkunden:&quot;</span>)</span>
<span id="cb3-116"><a href="#cb3-116" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span><span class="st">&#39;, &#39;</span><span class="sc">.</span>join(STAMMKUNDEN)<span class="sc">}</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb3-117"><a href="#cb3-117" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-118"><a href="#cb3-118" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 1. Drei Saetze aus dem Gespraech, drei Ziele ---------------------</span></span>
<span id="cb3-119"><a href="#cb3-119" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-120"><a href="#cb3-120" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;1. Was im Gespraech gesagt wurde - drei naheliegende Ziele</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb3-121"><a href="#cb3-121" aria-hidden="true" tabindex="-1"></a> kopfzeile()</span>
<span id="cb3-122"><a href="#cb3-122" aria-hidden="true" tabindex="-1"></a> plaene <span class="op">=</span> {}</span>
<span id="cb3-123"><a href="#cb3-123" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, ziel <span class="kw">in</span> [(<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Umsatz maximieren</span><span class="ch">\&quot;</span><span class="st">&quot;</span>, UMSATZ),</span>
<span id="cb3-124"><a href="#cb3-124" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Maschinen besser auslasten</span><span class="ch">\&quot;</span><span class="st">&quot;</span>, STUNDEN),</span>
<span id="cb3-125"><a href="#cb3-125" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Deckungsbeitrag maximieren</span><span class="ch">\&quot;</span><span class="st">&quot;</span>, DECKUNGSBEITRAG)]:</span>
<span id="cb3-126"><a href="#cb3-126" aria-hidden="true" tabindex="-1"></a> plaene[name] <span class="op">=</span> waehle_aus(ziel)</span>
<span id="cb3-127"><a href="#cb3-127" aria-hidden="true" tabindex="-1"></a> zeile(name, plaene[name])</span>
<span id="cb3-128"><a href="#cb3-128" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-129"><a href="#cb3-129" aria-hidden="true" tabindex="-1"></a> umsatzplan <span class="op">=</span> kennzahlen(plaene[<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Umsatz maximieren</span><span class="ch">\&quot;</span><span class="st">&quot;</span>])</span>
<span id="cb3-130"><a href="#cb3-130" aria-hidden="true" tabindex="-1"></a> auslastungsplan <span class="op">=</span> kennzahlen(plaene[<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Maschinen besser auslasten</span><span class="ch">\&quot;</span><span class="st">&quot;</span>])</span>
<span id="cb3-131"><a href="#cb3-131" aria-hidden="true" tabindex="-1"></a> dbplan <span class="op">=</span> kennzahlen(plaene[<span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">Deckungsbeitrag maximieren</span><span class="ch">\&quot;</span><span class="st">&quot;</span>])</span>
<span id="cb3-132"><a href="#cb3-132" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-133"><a href="#cb3-133" aria-hidden="true" tabindex="-1"></a> mehr_umsatz <span class="op">=</span> umsatzplan[<span class="st">&quot;umsatz&quot;</span>] <span class="op">-</span> dbplan[<span class="st">&quot;umsatz&quot;</span>]</span>
<span id="cb3-134"><a href="#cb3-134" aria-hidden="true" tabindex="-1"></a> weniger_db <span class="op">=</span> dbplan[<span class="st">&quot;db&quot;</span>] <span class="op">-</span> umsatzplan[<span class="st">&quot;db&quot;</span>]</span>
<span id="cb3-135"><a href="#cb3-135" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Der Umsatzplan bringt </span><span class="sc">{</span>mehr_umsatz<span class="sc">:,.0f}</span><span class="ss"> EUR mehr Umsatz und &quot;</span></span>
<span id="cb3-136"><a href="#cb3-136" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>weniger_db<span class="sc">:,.0f}</span><span class="ss"> EUR WENIGER&quot;</span>)</span>
<span id="cb3-137"><a href="#cb3-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Deckungsbeitrag. Fuer </span><span class="sc">{</span>mehr_umsatz<span class="sc">:,.0f}</span><span class="ss"> EUR Umsatz werden also &quot;</span></span>
<span id="cb3-138"><a href="#cb3-138" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>weniger_db<span class="sc">:,.0f}</span><span class="ss"> EUR Ergebnis&quot;</span>)</span>
<span id="cb3-139"><a href="#cb3-139" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; aufgegeben - und </span><span class="sc">{</span>umsatzplan[<span class="st">&#39;stamm_abgelehnt&#39;</span>]<span class="sc">}</span><span class="ss"> von &quot;</span></span>
<span id="cb3-140"><a href="#cb3-140" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="bu">int</span>(IST_STAMM.<span class="bu">sum</span>())<span class="sc">}</span><span class="ss"> Stammkundenauftraegen dazu.&quot;</span>)</span>
<span id="cb3-141"><a href="#cb3-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb3-142"><a href="#cb3-142" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; </span><span class="ch">\&quot;</span><span class="st">Auslastung</span><span class="ch">\&quot;</span><span class="st"> ist der haeufigste Wunsch und der schaedlichste. Der&quot;</span>)</span>
<span id="cb3-143"><a href="#cb3-143" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; darauf optimierte Plan bringt </span><span class="sc">{</span>dbplan[<span class="st">&#39;db&#39;</span>] <span class="op">-</span> auslastungsplan[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR weniger &quot;</span></span>
<span id="cb3-144"><a href="#cb3-144" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Deckungsbeitrag und&quot;</span>)</span>
<span id="cb3-145"><a href="#cb3-145" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>dbplan[<span class="st">&#39;umsatz&#39;</span>] <span class="op">-</span> auslastungsplan[<span class="st">&#39;umsatz&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR weniger Umsatz als der &quot;</span></span>
<span id="cb3-146"><a href="#cb3-146" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;DB-Plan - und erreicht dabei&quot;</span>)</span>
<span id="cb3-147"><a href="#cb3-147" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; nicht einmal mehr Auslastung: alle drei liegen bei &quot;</span></span>
<span id="cb3-148"><a href="#cb3-148" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>dbplan[<span class="st">&#39;auslastung&#39;</span>]<span class="sc">:.0f}</span><span class="ss"> %.&quot;</span>)</span>
<span id="cb3-149"><a href="#cb3-149" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Eine volle Maschine ist eben kein Ziel, sondern ein Nebenprodukt.&quot;</span>)</span>
<span id="cb3-150"><a href="#cb3-150" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-151"><a href="#cb3-151" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 2. Ein Satz, drei Lesarten --------------------------------------</span></span>
<span id="cb3-152"><a href="#cb3-152" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-153"><a href="#cb3-153" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;2. </span><span class="ch">\&quot;</span><span class="st">Stammkunden duerfen wir nicht verlieren</span><span class="ch">\&quot;</span><span class="st"> - aber was heisst das?</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb3-154"><a href="#cb3-154" aria-hidden="true" tabindex="-1"></a> kopfzeile()</span>
<span id="cb3-155"><a href="#cb3-155" aria-hidden="true" tabindex="-1"></a> zeile(<span class="st">&quot;ohne die Regel (DB maximieren)&quot;</span>, waehle_aus(DECKUNGSBEITRAG))</span>
<span id="cb3-156"><a href="#cb3-156" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-157"><a href="#cb3-157" aria-hidden="true" tabindex="-1"></a> <span class="co"># Lesart A: jeder Stammkundenauftrag wird angenommen</span></span>
<span id="cb3-158"><a href="#cb3-158" aria-hidden="true" tabindex="-1"></a> alle_stamm <span class="op">=</span> [[<span class="fl">1.0</span> <span class="cf">if</span> IST_STAMM[i] <span class="cf">else</span> <span class="fl">0.0</span> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="bu">len</span>(AUFTRAEGE))]]</span>
<span id="cb3-159"><a href="#cb3-159" aria-hidden="true" tabindex="-1"></a> lesart_a <span class="op">=</span> waehle_aus(DECKUNGSBEITRAG, zusatz_eq<span class="op">=</span>alle_stamm,</span>
<span id="cb3-160"><a href="#cb3-160" aria-hidden="true" tabindex="-1"></a> zusatz_b_eq<span class="op">=</span>[<span class="bu">float</span>(IST_STAMM.<span class="bu">sum</span>())])</span>
<span id="cb3-161"><a href="#cb3-161" aria-hidden="true" tabindex="-1"></a> zeile(<span class="st">&quot;A: jeden Stammauftrag annehmen&quot;</span>, lesart_a)</span>
<span id="cb3-162"><a href="#cb3-162" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-163"><a href="#cb3-163" aria-hidden="true" tabindex="-1"></a> <span class="co"># Lesart B: kein Stammkunde geht leer aus (mindestens ein Auftrag je Kunde)</span></span>
<span id="cb3-164"><a href="#cb3-164" aria-hidden="true" tabindex="-1"></a> je_kunde_ub, je_kunde_b <span class="op">=</span> [], []</span>
<span id="cb3-165"><a href="#cb3-165" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> kunde <span class="kw">in</span> STAMMKUNDEN:</span>
<span id="cb3-166"><a href="#cb3-166" aria-hidden="true" tabindex="-1"></a> je_kunde_ub.append([<span class="op">-</span><span class="fl">1.0</span> <span class="cf">if</span> a[<span class="dv">1</span>] <span class="op">==</span> kunde <span class="cf">else</span> <span class="fl">0.0</span> <span class="cf">for</span> a <span class="kw">in</span> AUFTRAEGE])</span>
<span id="cb3-167"><a href="#cb3-167" aria-hidden="true" tabindex="-1"></a> je_kunde_b.append(<span class="op">-</span><span class="fl">1.0</span>)</span>
<span id="cb3-168"><a href="#cb3-168" aria-hidden="true" tabindex="-1"></a> lesart_b <span class="op">=</span> waehle_aus(DECKUNGSBEITRAG, zusatz_ub<span class="op">=</span>je_kunde_ub,</span>
<span id="cb3-169"><a href="#cb3-169" aria-hidden="true" tabindex="-1"></a> zusatz_b_ub<span class="op">=</span>je_kunde_b)</span>
<span id="cb3-170"><a href="#cb3-170" aria-hidden="true" tabindex="-1"></a> zeile(<span class="st">&quot;B: kein Stammkunde geht leer aus&quot;</span>, lesart_b)</span>
<span id="cb3-171"><a href="#cb3-171" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-172"><a href="#cb3-172" aria-hidden="true" tabindex="-1"></a> a, b <span class="op">=</span> kennzahlen(lesart_a), kennzahlen(lesart_b)</span>
<span id="cb3-173"><a href="#cb3-173" aria-hidden="true" tabindex="-1"></a> ohne <span class="op">=</span> kennzahlen(waehle_aus(DECKUNGSBEITRAG))</span>
<span id="cb3-174"><a href="#cb3-174" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st"> Beide Lesarten erreichen dasselbe: kein Stammkunde geht leer aus.&quot;</span>)</span>
<span id="cb3-175"><a href="#cb3-175" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Lesart A kostet dafuer </span><span class="sc">{</span>ohne[<span class="st">&#39;db&#39;</span>] <span class="op">-</span> a[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR Deckungsbeitrag, &quot;</span></span>
<span id="cb3-176"><a href="#cb3-176" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Lesart B nur </span><span class="sc">{</span>ohne[<span class="st">&#39;db&#39;</span>] <span class="op">-</span> b[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR -&quot;</span>)</span>
<span id="cb3-177"><a href="#cb3-177" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; ein Unterschied von </span><span class="sc">{</span>b[<span class="st">&#39;db&#39;</span>] <span class="op">-</span> a[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR fuer denselben Satz aus derselben&quot;</span>)</span>
<span id="cb3-178"><a href="#cb3-178" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Besprechung. Wer nicht nachfragt, entscheidet das selbst, ohne es&quot;</span>)</span>
<span id="cb3-179"><a href="#cb3-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; zu merken.&quot;</span>)</span>
<span id="cb3-180"><a href="#cb3-180" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-181"><a href="#cb3-181" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 3. Die weiche Variante ------------------------------------------</span></span>
<span id="cb3-182"><a href="#cb3-182" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-183"><a href="#cb3-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;3. Dieselbe Regel als Strafkosten je abgelehntem Stammauftrag</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb3-184"><a href="#cb3-184" aria-hidden="true" tabindex="-1"></a> kopfzeile()</span>
<span id="cb3-185"><a href="#cb3-185" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> strafe <span class="kw">in</span> (<span class="dv">3_000</span>, <span class="dv">6_000</span>, <span class="dv">10_000</span>):</span>
<span id="cb3-186"><a href="#cb3-186" aria-hidden="true" tabindex="-1"></a> ziel <span class="op">=</span> DECKUNGSBEITRAG <span class="op">+</span> np.where(IST_STAMM, <span class="bu">float</span>(strafe), <span class="fl">0.0</span>)</span>
<span id="cb3-187"><a href="#cb3-187" aria-hidden="true" tabindex="-1"></a> zeile(<span class="ss">f&quot;C: Strafe </span><span class="sc">{</span>strafe<span class="sc">:&gt;6,}</span><span class="ss"> EUR je Auftrag&quot;</span>, waehle_aus(ziel))</span>
<span id="cb3-188"><a href="#cb3-188" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-189"><a href="#cb3-189" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st"> Bei 3.000 EUR aendert sich nichts: Die Strafe liegt unter dem, was&quot;</span>)</span>
<span id="cb3-190"><a href="#cb3-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; die Auftraege an Deckungsbeitrag verdraengen wuerden. Bei 10.000 EUR&quot;</span>)</span>
<span id="cb3-191"><a href="#cb3-191" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; kommt genau Lesart A heraus. Eine harte Regel ist also nichts&quot;</span>)</span>
<span id="cb3-192"><a href="#cb3-192" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; anderes als eine weiche mit hinreichend grosser Strafe - was ein&quot;</span>)</span>
<span id="cb3-193"><a href="#cb3-193" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Strafgewicht sonst noch bedeutet, steht im Kapitel CP-SAT.&quot;</span>)</span>
<span id="cb3-194"><a href="#cb3-194" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-195"><a href="#cb3-195" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 4. Was die harte Lesart ein Quartal spaeter anrichtet -----------</span></span>
<span id="cb3-196"><a href="#cb3-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-197"><a href="#cb3-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;4. Ein Quartal spaeter: Weber fragt einen Grossauftrag an</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb3-198"><a href="#cb3-198" aria-hidden="true" tabindex="-1"></a> erweitert <span class="op">=</span> AUFTRAEGE <span class="op">+</span> [(<span class="st">&quot;A-115&quot;</span>, <span class="st">&quot;Weber GmbH&quot;</span>, <span class="va">True</span>, <span class="dv">320</span>, <span class="dv">210_000</span>, <span class="dv">150_000</span>)]</span>
<span id="cb3-199"><a href="#cb3-199" aria-hidden="true" tabindex="-1"></a> stunden_neu <span class="op">=</span> np.array([a[<span class="dv">3</span>] <span class="cf">for</span> a <span class="kw">in</span> erweitert], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb3-200"><a href="#cb3-200" aria-hidden="true" tabindex="-1"></a> db_neu <span class="op">=</span> np.array([a[<span class="dv">4</span>] <span class="op">-</span> a[<span class="dv">5</span>] <span class="cf">for</span> a <span class="kw">in</span> erweitert], dtype<span class="op">=</span><span class="bu">float</span>)</span>
<span id="cb3-201"><a href="#cb3-201" aria-hidden="true" tabindex="-1"></a> stamm_neu <span class="op">=</span> np.array([a[<span class="dv">2</span>] <span class="cf">for</span> a <span class="kw">in</span> erweitert])</span>
<span id="cb3-202"><a href="#cb3-202" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-203"><a href="#cb3-203" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; A-115: Weber GmbH, </span><span class="sc">{</span>erweitert[<span class="op">-</span><span class="dv">1</span>][<span class="dv">3</span>]<span class="sc">}</span><span class="ss"> Maschinenstunden - allein mehr,&quot;</span>)</span>
<span id="cb3-204"><a href="#cb3-204" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; als der ganze Zeitraum hergibt (</span><span class="sc">{</span>KAPAZITAET<span class="sc">}</span><span class="ss"> h).</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb3-205"><a href="#cb3-205" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-206"><a href="#cb3-206" aria-hidden="true" tabindex="-1"></a> alle_stamm_neu <span class="op">=</span> [[<span class="fl">1.0</span> <span class="cf">if</span> stamm_neu[i] <span class="cf">else</span> <span class="fl">0.0</span> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="bu">len</span>(erweitert))]]</span>
<span id="cb3-207"><a href="#cb3-207" aria-hidden="true" tabindex="-1"></a> hart <span class="op">=</span> waehle_aus(db_neu, zusatz_eq<span class="op">=</span>alle_stamm_neu,</span>
<span id="cb3-208"><a href="#cb3-208" aria-hidden="true" tabindex="-1"></a> zusatz_b_eq<span class="op">=</span>[<span class="bu">float</span>(stamm_neu.<span class="bu">sum</span>())],</span>
<span id="cb3-209"><a href="#cb3-209" aria-hidden="true" tabindex="-1"></a> auftraege<span class="op">=</span>erweitert, stunden<span class="op">=</span>stunden_neu)</span>
<span id="cb3-210"><a href="#cb3-210" aria-hidden="true" tabindex="-1"></a> weich <span class="op">=</span> waehle_aus(db_neu <span class="op">+</span> np.where(stamm_neu, <span class="fl">10_000.0</span>, <span class="fl">0.0</span>),</span>
<span id="cb3-211"><a href="#cb3-211" aria-hidden="true" tabindex="-1"></a> auftraege<span class="op">=</span>erweitert, stunden<span class="op">=</span>stunden_neu)</span>
<span id="cb3-212"><a href="#cb3-212" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-213"><a href="#cb3-213" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Lesart A (hart): &quot;</span></span>
<span id="cb3-214"><a href="#cb3-214" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;UNZULAESSIG - der Nachtlauf bricht ab&#39;</span> <span class="cf">if</span> hart <span class="kw">is</span> <span class="va">None</span> <span class="cf">else</span> <span class="st">&#39;loesbar&#39;</span><span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-215"><a href="#cb3-215" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> weich <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb3-216"><a href="#cb3-216" aria-hidden="true" tabindex="-1"></a> k <span class="op">=</span> kennzahlen(weich, erweitert, stunden_neu,</span>
<span id="cb3-217"><a href="#cb3-217" aria-hidden="true" tabindex="-1"></a> np.array([a[<span class="dv">4</span>] <span class="cf">for</span> a <span class="kw">in</span> erweitert], dtype<span class="op">=</span><span class="bu">float</span>),</span>
<span id="cb3-218"><a href="#cb3-218" aria-hidden="true" tabindex="-1"></a> db_neu, stamm_neu)</span>
<span id="cb3-219"><a href="#cb3-219" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Lesart C (weich): loesbar - </span><span class="sc">{</span>k[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR Deckungsbeitrag, &quot;</span></span>
<span id="cb3-220"><a href="#cb3-220" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>k[<span class="st">&#39;stamm_abgelehnt&#39;</span>]<span class="sc">}</span><span class="ss"> Stammauftrag abgelehnt&quot;</span>)</span>
<span id="cb3-221"><a href="#cb3-221" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-222"><a href="#cb3-222" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-223"><a href="#cb3-223" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WORAUF ES ANKOMMT&quot;</span>)</span>
<span id="cb3-224"><a href="#cb3-224" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb3-225"><a href="#cb3-225" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Die Daten waren in allen vier Teilen dieselben. Was sich geaendert hat,&quot;</span>)</span>
<span id="cb3-226"><a href="#cb3-226" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;war nur die Frage - und mit ihr der Plan, der Deckungsbeitrag und die&quot;</span>)</span>
<span id="cb3-227"><a href="#cb3-227" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Kundenbeziehung.&quot;</span>)</span>
<span id="cb3-228"><a href="#cb3-228" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb3-229"><a href="#cb3-229" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:&quot;</span>)</span>
<span id="cb3-230"><a href="#cb3-230" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>()</span>
<span id="cb3-231"><a href="#cb3-231" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; 1. WORAN wird der Erfolg gemessen? Nicht &#39;was ist Ihnen wichtig&#39;,&quot;</span>)</span>
<span id="cb3-232"><a href="#cb3-232" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; sondern: Welche Zahl steht am Jahresende im Bericht? Umsatz,&quot;</span>)</span>
<span id="cb3-233"><a href="#cb3-233" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Auslastung und Deckungsbeitrag fuehren hier zu drei&quot;</span>)</span>
<span id="cb3-234"><a href="#cb3-234" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; verschiedenen Plaenen.&quot;</span>)</span>
<span id="cb3-235"><a href="#cb3-235" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; 2. WAS GENAU heisst der Satz? &#39;Stammkunden nicht verlieren&#39; hat&quot;</span>)</span>
<span id="cb3-236"><a href="#cb3-236" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; mindestens drei Lesarten mit </span><span class="sc">{</span>b[<span class="st">&#39;db&#39;</span>] <span class="op">-</span> a[<span class="st">&#39;db&#39;</span>]<span class="sc">:,.0f}</span><span class="ss"> EUR Unterschied. Die Frage&quot;</span>)</span>
<span id="cb3-237"><a href="#cb3-237" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; &#39;meinen Sie jeden Auftrag oder jeden Kunden?&#39; dauert zehn&quot;</span>)</span>
<span id="cb3-238"><a href="#cb3-238" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Sekunden.&quot;</span>)</span>
<span id="cb3-239"><a href="#cb3-239" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; 3. WAS PASSIERT, WENN ES NICHT GEHT? Eine harte Regel, die niemand&quot;</span>)</span>
<span id="cb3-240"><a href="#cb3-240" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; hinterfragt hat, macht das System eines Tages unloesbar - und&quot;</span>)</span>
<span id="cb3-241"><a href="#cb3-241" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; zwar genau dann, wenn ein besonders grosser Auftrag hereinkommt.&quot;</span>)</span>
<span id="cb3-242"><a href="#cb3-242" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Weiche Regeln antworten auch dann noch.&quot;</span>)</span>
<span id="cb3-243"><a href="#cb3-243" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe:</strong></p>
<pre><code>====================================================================================
VOM MANAGEMENT-WUNSCH ZUM MODELL
====================================================================================
14 Anfragen verlangen 800 Maschinenstunden, verfuegbar sind 300.
Es muss also ausgewaehlt werden. 3 der Kunden sind Stammkunden:
Kranz AG, Obermeier KG, Weber GmbH
------------------------------------------------------------------------------------
1. Was im Gespraech gesagt wurde - drei naheliegende Ziele
Umsatz DB Aus- Stamm- Kunden ange-
lastung auftr. ohne nommen
------------------------------------------------------------------------------
&quot;Umsatz maximieren&quot; 227,000 66,000 100% 5 2 6
&quot;Maschinen besser auslasten&quot; 205,000 54,000 100% 4 1 5
&quot;Deckungsbeitrag maximieren&quot; 222,000 78,500 100% 2 1 8
Grafik-Upgrade Stufe 1: elf neue Diagramme Grafik_Upgrade.md verlangt "37 didaktisch wertvolle Visualisierungen". Es sind 19: Die Abschnitte "Modul 1-4" wiederholen die Grafiken 1-18 unter den Nummern 19-36, nur Grafik 37 ist zusaetzlich. Belegt an den Skriptnamen - 38 Nennungen, 19 verschiedene Dateien, erzeuge_kombinatorik_wand.py steht dreimal darin. Von den 19 betreffen neun bereits vorhandene Diagramme. Umgesetzt sind die zehn wirklich neuen plus eine elfte, die aus einem Sachfehler des Auftrags entstand: Grafik 14 sollte in Abschnitt 12.3 stehen, nannte aber Zahlen aus Kapitel 17. Beide Stellen haben jetzt ihre eigene Grafik mit ihren eigenen Zahlen. Vier Vorgaben des Auftrags wurden bewusst nicht befolgt: PNG bei 300 dpi (das Buch kennt nur SVG - die 33 verwaisten PNGs waren gerade erst geloescht worden), Matplotlib-Standardfarben (die Buchpalette steht in 33 Diagrammen), harte Kapitelnummern im Fliesstext ({ref:sec:...} stattdessen) und die fehlende Byte-Reproduzierbarkeit. Der eigentliche Ertrag waren die Selbsttests. Jeder Generator prueft seine Zahlen gegen das, was im Kapitel steht, und bricht bei Abweichung ab. Vier haben angeschlagen: * Kombinatorik-Wand: 25! ergibt 491,5 Mio. Jahre, die Tabelle nennt 490 Mio. - Rundung auf zwei signifikante Stellen, nicht Fehler des Buchs. * Laufzeit-Diagramm: Die abgedruckte Prozentspalte laesst sich aus den abgedruckten Zeiten nicht exakt nachrechnen (0,001/0,008 ergibt 12 %, gedruckt sind 14 %) - die Zeiten sind gerundet, die Prozente nicht. * Predict-then-Optimize: geratene Datenaufteilung ergab 197,35 statt 200,95 MSE. Mit TRAINING aus dem Buchprogramm stimmen alle vier Kostenwerte. * Almgren-Chriss: ein echter Fehler im Buchprogramm. DER FUND: analytische_loesung() minimiert die eigene Kostenfunktion nicht. Ihr Pfad kostet 10.860,40 EUR, der DP-Pfad 10.266,24 EUR - ein Gitterverfahren kann das kontinuierliche Optimum aber nicht unterbieten. periodenkosten() rechnet das Risiko mit lambda/2, die geschlossene Formel setzt kappa~^2 = lambda*sigma^2*P0^2/eta ohne dieses Halbe. Mit lambda/2 liefert die Formel 10.264,85 EUR und denselben Pfad wie die DP, knapp UNTER der Gitterloesung. Die Grafik zeigt bis zur Entscheidung darueber den DP-Pfad; er ist der, den das Kapitel abdruckt. Nachweise: Alle elf Diagramme sind ueber zwei Laeufe byte-identisch. Kein vorhandenes Diagramm wurde veraendert. Die Lastabwurf-Grafik reproduziert die Befundtabelle exakt (874.870/2.504.154/28 von 40 gegen 351.356/415.924/0). Zwei Abbildungen landeten beim automatischen Einfuegen im Codeblock (ihre Anker standen in abgedruckten Ausgaben) und wurden dahinter verschoben; eine Pruefung ueber alle elf zeigt null Fehlplatzierungen. Neu: bilder_04/stil_04.py buendelt Palette, rcParams und speichere(). networkx wurde entgegen der Planung nicht gebraucht - pyproject.toml bleibt unveraendert. PDF von 781 auf 788 Seiten, 44 Diagramme, 27 Generatoren. Veroeffentlicht: 25 Dateien. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 01:11:33 +02:00
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
Der Umsatzplan bringt 5,000 EUR mehr Umsatz und 12,500 EUR WENIGER
Deckungsbeitrag. Fuer 5,000 EUR Umsatz werden also 12,500 EUR Ergebnis
aufgegeben - und 5 von 6 Stammkundenauftraegen dazu.
&quot;Auslastung&quot; ist der haeufigste Wunsch und der schaedlichste. Der
darauf optimierte Plan bringt 24,500 EUR weniger Deckungsbeitrag und
17,000 EUR weniger Umsatz als der DB-Plan - und erreicht dabei
nicht einmal mehr Auslastung: alle drei liegen bei 100 %.
Eine volle Maschine ist eben kein Ziel, sondern ein Nebenprodukt.
------------------------------------------------------------------------------------
2. &quot;Stammkunden duerfen wir nicht verlieren&quot; - aber was heisst das?
Umsatz DB Aus- Stamm- Kunden ange-
lastung auftr. ohne nommen
------------------------------------------------------------------------------
ohne die Regel (DB maximieren) 222,000 78,500 100% 2 1 8
A: jeden Stammauftrag annehmen 181,000 66,000 90% 0 0 8
B: kein Stammkunde geht leer aus 213,000 75,000 98% 2 0 8
Beide Lesarten erreichen dasselbe: kein Stammkunde geht leer aus.
Lesart A kostet dafuer 12,500 EUR Deckungsbeitrag, Lesart B nur 3,500 EUR -
ein Unterschied von 9,000 EUR fuer denselben Satz aus derselben
Besprechung. Wer nicht nachfragt, entscheidet das selbst, ohne es
zu merken.
------------------------------------------------------------------------------------
3. Dieselbe Regel als Strafkosten je abgelehntem Stammauftrag
Umsatz DB Aus- Stamm- Kunden ange-
lastung auftr. ohne nommen
------------------------------------------------------------------------------
C: Strafe 3,000 EUR je Auftrag 222,000 78,500 100% 2 1 8
C: Strafe 6,000 EUR je Auftrag 205,000 73,000 100% 1 0 8
C: Strafe 10,000 EUR je Auftrag 181,000 66,000 90% 0 0 8
Bei 3.000 EUR aendert sich nichts: Die Strafe liegt unter dem, was
die Auftraege an Deckungsbeitrag verdraengen wuerden. Bei 10.000 EUR
kommt genau Lesart A heraus. Eine harte Regel ist also nichts
anderes als eine weiche mit hinreichend grosser Strafe - was ein
Strafgewicht sonst noch bedeutet, steht im Kapitel CP-SAT.
------------------------------------------------------------------------------------
4. Ein Quartal spaeter: Weber fragt einen Grossauftrag an
A-115: Weber GmbH, 320 Maschinenstunden - allein mehr,
als der ganze Zeitraum hergibt (300 h).
Lesart A (hart): UNZULAESSIG - der Nachtlauf bricht ab
Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt
====================================================================================
WORAUF ES ANKOMMT
====================================================================================
Die Daten waren in allen vier Teilen dieselben. Was sich geaendert hat,
war nur die Frage - und mit ihr der Plan, der Deckungsbeitrag und die
Kundenbeziehung.
Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:
1. WORAN wird der Erfolg gemessen? Nicht &#39;was ist Ihnen wichtig&#39;,
sondern: Welche Zahl steht am Jahresende im Bericht? Umsatz,
Auslastung und Deckungsbeitrag fuehren hier zu drei
verschiedenen Plaenen.
2. WAS GENAU heisst der Satz? &#39;Stammkunden nicht verlieren&#39; hat
mindestens drei Lesarten mit 9,000 EUR Unterschied. Die Frage
&#39;meinen Sie jeden Auftrag oder jeden Kunden?&#39; dauert zehn
Sekunden.
3. WAS PASSIERT, WENN ES NICHT GEHT? Eine harte Regel, die niemand
hinterfragt hat, macht das System eines Tages unloesbar - und
zwar genau dann, wenn ein besonders grosser Auftrag hereinkommt.
Weiche Regeln antworten auch dann noch.
====================================================================================</code></pre>
Grafik-Upgrade Stufe 1: elf neue Diagramme Grafik_Upgrade.md verlangt "37 didaktisch wertvolle Visualisierungen". Es sind 19: Die Abschnitte "Modul 1-4" wiederholen die Grafiken 1-18 unter den Nummern 19-36, nur Grafik 37 ist zusaetzlich. Belegt an den Skriptnamen - 38 Nennungen, 19 verschiedene Dateien, erzeuge_kombinatorik_wand.py steht dreimal darin. Von den 19 betreffen neun bereits vorhandene Diagramme. Umgesetzt sind die zehn wirklich neuen plus eine elfte, die aus einem Sachfehler des Auftrags entstand: Grafik 14 sollte in Abschnitt 12.3 stehen, nannte aber Zahlen aus Kapitel 17. Beide Stellen haben jetzt ihre eigene Grafik mit ihren eigenen Zahlen. Vier Vorgaben des Auftrags wurden bewusst nicht befolgt: PNG bei 300 dpi (das Buch kennt nur SVG - die 33 verwaisten PNGs waren gerade erst geloescht worden), Matplotlib-Standardfarben (die Buchpalette steht in 33 Diagrammen), harte Kapitelnummern im Fliesstext ({ref:sec:...} stattdessen) und die fehlende Byte-Reproduzierbarkeit. Der eigentliche Ertrag waren die Selbsttests. Jeder Generator prueft seine Zahlen gegen das, was im Kapitel steht, und bricht bei Abweichung ab. Vier haben angeschlagen: * Kombinatorik-Wand: 25! ergibt 491,5 Mio. Jahre, die Tabelle nennt 490 Mio. - Rundung auf zwei signifikante Stellen, nicht Fehler des Buchs. * Laufzeit-Diagramm: Die abgedruckte Prozentspalte laesst sich aus den abgedruckten Zeiten nicht exakt nachrechnen (0,001/0,008 ergibt 12 %, gedruckt sind 14 %) - die Zeiten sind gerundet, die Prozente nicht. * Predict-then-Optimize: geratene Datenaufteilung ergab 197,35 statt 200,95 MSE. Mit TRAINING aus dem Buchprogramm stimmen alle vier Kostenwerte. * Almgren-Chriss: ein echter Fehler im Buchprogramm. DER FUND: analytische_loesung() minimiert die eigene Kostenfunktion nicht. Ihr Pfad kostet 10.860,40 EUR, der DP-Pfad 10.266,24 EUR - ein Gitterverfahren kann das kontinuierliche Optimum aber nicht unterbieten. periodenkosten() rechnet das Risiko mit lambda/2, die geschlossene Formel setzt kappa~^2 = lambda*sigma^2*P0^2/eta ohne dieses Halbe. Mit lambda/2 liefert die Formel 10.264,85 EUR und denselben Pfad wie die DP, knapp UNTER der Gitterloesung. Die Grafik zeigt bis zur Entscheidung darueber den DP-Pfad; er ist der, den das Kapitel abdruckt. Nachweise: Alle elf Diagramme sind ueber zwei Laeufe byte-identisch. Kein vorhandenes Diagramm wurde veraendert. Die Lastabwurf-Grafik reproduziert die Befundtabelle exakt (874.870/2.504.154/28 von 40 gegen 351.356/415.924/0). Zwei Abbildungen landeten beim automatischen Einfuegen im Codeblock (ihre Anker standen in abgedruckten Ausgaben) und wurden dahinter verschoben; eine Pruefung ueber alle elf zeigt null Fehlplatzierungen. Neu: bilder_04/stil_04.py buendelt Palette, rcParams und speichere(). networkx wurde entgegen der Planung nicht gebraucht - pyproject.toml bleibt unveraendert. PDF von 781 auf 788 Seiten, 44 Diagramme, 27 Generatoren. Veroeffentlicht: 25 Dateien. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 01:11:33 +02:00
<figure>
<img src="bilder_04/kap_modellierung_zielgroessen.svg" alt="Abb. 4.1: Die drei Pläne aus der Tabelle, je Achse auf den besten der drei bezogen. Erzeugt von bilder_04/erzeuge_zielgroessen_radar.py." />
<figcaption aria-hidden="true">Abb. 4.1: Die drei Pläne aus der Tabelle, je Achse auf den besten der drei bezogen. Erzeugt von <code>bilder_04/erzeuge_zielgroessen_radar.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Jeder Plan gewinnt auf seiner eigenen Achse — das ist keine Überraschung, sondern die Definition. Interessant ist, was daneben passiert: Der Auslastungsplan liegt bei der Auslastung gleichauf mit den anderen und verliert beim Deckungsbeitrag ein knappes Drittel. ### Teil 1: Drei Ziele, drei Pläne</p>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<table>
<colgroup>
<col style="width: 15%" />
<col style="width: 21%" />
<col style="width: 21%" />
<col style="width: 21%" />
<col style="width: 21%" />
</colgroup>
<thead>
<tr class="header">
<th>Zielgröße</th>
<th style="text-align: right;">Umsatz</th>
<th style="text-align: right;">Deckungsbeitrag</th>
<th style="text-align: right;">Auslastung</th>
<th style="text-align: right;">abgelehnte Stammaufträge</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>„Umsatz maximieren”</td>
<td style="text-align: right;"><strong>227 000 €</strong></td>
<td style="text-align: right;">66 000 €</td>
<td style="text-align: right;">100 %</td>
<td style="text-align: right;">5 von 6</td>
</tr>
<tr class="even">
<td>„Maschinen besser auslasten”</td>
<td style="text-align: right;">205 000 €</td>
<td style="text-align: right;">54 000 €</td>
<td style="text-align: right;">100 %</td>
<td style="text-align: right;">4 von 6</td>
</tr>
<tr class="odd">
<td>„Deckungsbeitrag maximieren”</td>
<td style="text-align: right;">222 000 €</td>
<td style="text-align: right;"><strong>78 500 €</strong></td>
<td style="text-align: right;">100 %</td>
<td style="text-align: right;">2 von 6</td>
</tr>
</tbody>
</table>
<p>Zwei Beobachtungen, die im Gespräch niemand vorhergesagt hätte:</p>
<p><strong>Der Umsatzplan kostet 12 500 € Ergebnis, um 5 000 € Umsatz zu gewinnen</strong> — und nimmt dabei fünf von sechs Stammkundenaufträgen aus dem Plan. Das Wechselkursverhältnis ist 1 : 2,5 gegen den Betrieb.</p>
<p><strong>Der Auslastungsplan ist in jeder Hinsicht der schlechteste — auch bei der Auslastung.</strong> Alle drei Pläne belegen die Maschinen zu 100 %; der eigens darauf optimierte Plan erreicht nur nicht mehr, sondern verliert dabei 24 500 € Deckungsbeitrag. Der Grund ist einfach: Bei knapper Kapazität ist eine volle Maschine keine Leistung, sondern die Voreinstellung. Zu optimieren gibt es nur, <strong>womit</strong> sie voll wird.</p>
<blockquote>
<p><strong>⚠️ Auslastung ist ein Ergebnis, kein Ziel</strong></p>
<p>„Wir müssen die Maschinen besser auslasten” ist der häufigste Satz in Produktionsbesprechungen und fast immer die falsche Zielgröße. Auslastung misst, wie viel Zeit vergeht — nicht, was dabei herauskommt. Wer sie maximiert, belohnt lange, schlecht bezahlte Aufträge.</p>
<p>Die Ausnahme, die die Regel erklärt: Wenn Maschinen tatsächlich leer stehen, <em>weil</em> zu wenig hereinkommt, ist Auslastung ein sinnvolles <strong>Symptom</strong> — aber dann liegt das Problem im Vertrieb und nicht in der Planung.</p>
</blockquote>
<h3 id="teil-2-ein-satz-drei-lesarten">Teil 2: Ein Satz, drei Lesarten</h3>
<p>„Stammkunden dürfen wir nicht verlieren.” Jeder im Raum nickt. Niemand merkt, dass drei verschiedene Modelle gemeint sein können:</p>
<table>
<thead>
<tr class="header">
<th>Lesart</th>
<th style="text-align: right;">Deckungsbeitrag</th>
<th style="text-align: right;">kostet gegenüber „ohne Regel”</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>ohne die Regel</td>
<td style="text-align: right;">78 500 €</td>
<td style="text-align: right;"></td>
</tr>
<tr class="even">
<td><strong>A:</strong> jeden Stammkundenauftrag annehmen</td>
<td style="text-align: right;">66 000 €</td>
<td style="text-align: right;">12 500 €</td>
</tr>
<tr class="odd">
<td><strong>B:</strong> kein Stammkunde geht leer aus</td>
<td style="text-align: right;">75 000 €</td>
<td style="text-align: right;">3 500 €</td>
</tr>
</tbody>
</table>
<p><strong>Beide Lesarten erreichen dasselbe Ziel</strong> — kein Stammkunde geht leer aus. Lesart A kostet dafür das Dreieinhalbfache. Der Unterschied von 9 000 € entsteht durch eine Frage, die zehn Sekunden dauert:</p>
<blockquote>
<p><em>„Meinen Sie jeden Auftrag oder jeden Kunden?“</em></p>
</blockquote>
<p>Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.</p>
<h3 id="teil-3-hart-oder-weich">Teil 3: Hart oder weich</h3>
<p>Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:</p>
<table>
<thead>
<tr class="header">
<th style="text-align: right;">Strafe je Auftrag</th>
<th style="text-align: right;">Deckungsbeitrag</th>
<th style="text-align: right;">abgelehnte Stammaufträge</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;">3 000 €</td>
<td style="text-align: right;">78 500 €</td>
<td style="text-align: right;">2</td>
</tr>
<tr class="even">
<td style="text-align: right;">6 000 €</td>
<td style="text-align: right;">73 000 €</td>
<td style="text-align: right;">1</td>
</tr>
<tr class="odd">
<td style="text-align: right;">10 000 €</td>
<td style="text-align: right;">66 000 €</td>
<td style="text-align: right;">0</td>
</tr>
</tbody>
</table>
<p>Bei 3 000 € ändert sich <strong>nichts</strong>: Die Strafe liegt unter dem Deckungsbeitrag, den die Stammkundenaufträge verdrängen würden. Bei 10 000 € kommt genau Lesart A heraus.</p>
Phase 6.3: Parallelitaet und Determinismus - gemessen statt behauptet Neuer Abschnitt im CP-SAT-Kapitel plus Parallele_Suche.py (76. Programm). Setzt Paket 2 aus Verbesserungen_02.md um. Was fehlte, war nicht die Regel, sondern die Messung: Das Buch setzt an acht Stellen num_workers = 1 mit dem Kommentar "fuer eine reproduzierbare Ausgabe", nachgeprueft hatte es nie jemand. Gerechnet wird auf demselben Job-Shop wie das Intervallvariablen-Kapitel, nur gross genug, dass die Suche arbeitet: 12 Auftraege, 10 Maschinen, 120 Arbeitsgaenge. Zwei Befunde, beide staerker als die Behauptung: * Die Beschleunigung ist ueberlinear. Acht Arbeiter waren im abgedruckten Lauf nicht achtmal, sondern 12,3-mal schneller als einer. Kein Messfehler: CP-SAT vervielfacht nicht dieselbe Suche, sondern laesst verschiedene Strategien nebeneinander laufen, die einander ihre Schranken mitteilen. * Der Seed genuegt nicht - und zwar schon ab ZWEI Arbeitern. Ein Arbeiter: 1 Plan aus 4 Laeufen. Zwei Arbeiter: 3 verschiedene Plaene aus 4 Laeufen, bei identischem random_seed und identischem Zielwert 183. Die vollstaendige Antwort kam erst ueber die Uebungsaufgabe: Mit num_workers = 1, aber fuenf verschiedenen Seeds ergeben sich ebenfalls fuenf verschiedene Plaene. Keiner der beiden Parameter sichert die Reproduzierbarkeit allein - erst die Kombination traegt. Weiter fuer die Aufgabe gemessen: Der Gewinn kehrt sich um (auf 24 Kernen Faktor 10,2 bei 8 Arbeitern, 12,3 bei 16, 9,0 bei 24) - "so viele Arbeiter wie Kerne" ist damit widerlegt. Und bei 15 Auftraegen laeuft ein Arbeiter ins 60-s-Limit (FEASIBLE, Makespan 200), waehrend acht OPTIMAL mit demselben Makespan 200 nach 26,4 s melden: Der Unterschied liegt nicht in der Loesung, sondern im Beweis, dass es keine bessere gibt. Die abgedruckte Ausgabe traegt die Kennzeichnung "Laufzeiten und die Zahl der verschiedenen Plaene sind hardwareabhaengig" - nach dem Muster, das das Testing-Kapitel fuer Benchmark_Skalierung.py schon verwendet. Der Vergleich des extrahierten Programms mit dem Abdruck weicht denn auch in genau einer Zelle ab (4 statt 3 verschiedene Plaene bei 4 Arbeitern); Zielwert und Struktur sind identisch. Hier ist die Nichtreproduzierbarkeit der abgedruckten Zahl die Aussage selbst. Mitgezogen: Kapitelkopf, Lernziele, Selbsttest, Zusammenfassung, Vorwort-Programmverzeichnis, Uebungsaufgabe und Loesung in Anhang A, ein Verweis aus dem bestehenden Callout zu mehrdeutigen Optima und einer aus Warmstart_Effekt.py im MILP-Kapitel (dort nach Regel 12 der Kapitelname). Stand: 295 Abschnitte, 730 Querverweise, 328 Indexmarken, 76 Programme, 140 Aufgaben mit 140 Loesungen, 33 pytest-Tests, PDF 744 Seiten, 69 netzfreie Programme fehlerfrei. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:19:53 +02:00
<p>Daraus folgt eine Einsicht, die das Modellieren erleichtert: <strong>Eine harte Regel ist eine weiche mit hinreichend großer Strafe.</strong> Man kann also immer weich anfangen und die Strafe hochdrehen, bis die Regel greift — und sieht dabei, was sie kostet. Was ein Strafgewicht darüber hinaus bedeutet und warum zwei Gewichte einen <em>Wechselkurs</em> festlegen und keine Wichtigkeiten, steht in <a href="cpsat.html#sec:cpsat-denkfehler">Abschnitt 7.10</a>.</p>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<h3 id="teil-4-ein-quartal-später">Teil 4: Ein Quartal später</h3>
<p>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.</p>
<pre><code>Lesart A (hart): UNZULAESSIG - der Nachtlauf bricht ab
Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt</code></pre>
<p>Das ist kein konstruierter Sonderfall, sondern der Normalfall: <strong>Harte Regeln scheitern nicht bei ihrer Einführung, sondern Monate später</strong> — und zwar bevorzugt dann, wenn etwas Ungewöhnliches passiert, also genau dann, wenn man die Planung am dringendsten braucht.</p>
<p>Die weiche Variante antwortet auch dann noch. Sie lehnt den Großauftrag ab, weist das im Bericht aus, und ein Mensch entscheidet. Das ist der ganze Unterschied — und er wird beim Schreiben der Anforderung festgelegt, nicht beim Programmieren.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Hart ist nur, was rechtlich oder physikalisch unmöglich ist. Alles andere wird weich mit hoher Strafe. Dann liefert der Solver den am wenigsten schlechten Plan statt einer Fehlermeldung — und die Fehlermeldung ist das, was niemand gebrauchen kann.</p>
</blockquote>
<hr />
<h2 id="sec:modellierung-leitfaden">4.5 Der Gesprächsleitfaden</h2>
<p>Was aus den vier Teilen als Handwerkszeug bleibt — Fragen, die man in der ersten Besprechung stellt, bevor eine Zeile Code entsteht:</p>
<blockquote>
<p><strong>📎 Zwölf Fragen für das erste Gespräch</strong></p>
<p><strong>Zur Entscheidung</strong> 1. Was genau wird entschieden — und von wem heute? 2. Was steht dabei <em>nicht</em> zur Disposition? 3. Wie oft wird entschieden, und wie viel Zeit ist dafür?</p>
<p><strong>Zum Ziel</strong> 4. Welche Zahl steht am Jahresende im Bericht? 5. Wenn genau diese Zahl maximiert würde — welcher Plan käme heraus, und wäre er akzeptabel? 6. Gibt es eine zweite Zahl, die auch stimmen muss? Was ist Ihnen ein Prozent der ersten wert, um ein Prozent der zweiten zu gewinnen?</p>
<p><strong>Zu den Regeln</strong> 7. Welche dieser Regeln ist gesetzlich oder physikalisch zwingend? 8. Welche würden Sie in einer Ausnahmesituation brechen — und was wäre eine Ausnahmesituation? 9. Bei „darf nicht”: Betrifft das jeden einzelnen Fall oder das Gesamtbild?</p>
<p><strong>Zum Betrieb</strong> 10. Was soll passieren, wenn es keine zulässige Lösung gibt? 11. Wer sieht das Ergebnis, und was muss er daran erkennen können? 12. Woran würden Sie in einem halben Jahr merken, dass das System nicht mehr das Richtige tut?</p>
<p>Frage 5 ist die wichtigste. Sie verwandelt eine abstrakte Zielangabe in einen konkreten Plan, den man ablehnen kann — und Ablehnung ist die einzige Form von Anforderungsklärung, die zuverlässig funktioniert.</p>
</blockquote>
<hr />
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<h2 id="sec:modellierung-glossar">4.6 Deutsch — OR: ein Übersetzungslexikon</h2>
<p>Die zwölf Fragen liefern Sätze. Sätze sind noch kein Modell — aber die meisten von ihnen haben eine <strong>Standardübersetzung</strong>, und wer sie kennt, spart sich das Nachdenken für die Fälle, in denen es keine gibt.</p>
<p>Die Tabelle liest sich von links nach rechts: Was in der Besprechung <strong>gesagt</strong> wird, was es <strong>bedeutet</strong>, und welcher <strong>Baustein</strong> daraus wird. Die B-Nummern verweisen auf <a href="anhang-modellierungsmuster.html#anhang-modellierungsmuster">Anhang B</a>, wo jedes Muster mit Formulierung und Code steht.</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>Was gesagt wird</th>
<th>Was gemeint ist</th>
<th>Baustein</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>„Wir haben nur 400 Stunden.”</td>
<td>eine Obergrenze, die nicht überschritten werden darf</td>
<td>harte Kapazitätsgrenze <span class="math inline">\le b</span> (<a href="lp.html#kap-lp">Kapitel 5</a>)</td>
</tr>
<tr class="even">
<td>„Mehr als zwei Millionen gibt es nicht.”</td>
<td>dasselbe, auf Geld</td>
<td>Budgetlimit (B27)</td>
</tr>
<tr class="odd">
<td>„Möglichst nicht über 40 Stunden.”</td>
<td>eine Grenze, die im Notfall gebrochen werden darf</td>
<td>weiche Grenze mit Strafkosten (B8)</td>
</tr>
<tr class="even">
<td>„Wir können nur ganze Kisten liefern.”</td>
<td>die Menge ist nicht teilbar</td>
<td>Ganzzahligkeitsbedingung (<a href="milp.html#kap-milp">Kapitel 6</a>)</td>
</tr>
<tr class="odd">
<td>„Wenn wir die Anlage nutzen, fallen Fixkosten an.”</td>
<td>Kosten, die erst ab der ersten Einheit anfallen</td>
<td>Aktivierungsschalter (B1)</td>
</tr>
<tr class="even">
<td>„Entweder gar nicht oder mindestens 50.”</td>
<td>kein Betrieb im Kleinen</td>
<td>semikontinuierliche Variable (B2)</td>
</tr>
<tr class="odd">
<td>„Wenn wir A bauen, brauchen wir auch B.”</td>
<td>eine Folgeverpflichtung</td>
<td>Implikation (B3)</td>
</tr>
<tr class="even">
<td>„Höchstens drei Positionen im Depot.”</td>
<td>eine Obergrenze auf die <strong>Anzahl</strong>, nicht die Menge</td>
<td>Kardinalität (B6)</td>
</tr>
<tr class="odd">
<td>„Der Anteil muss mindestens 30 % betragen.”</td>
<td>eine Bedingung auf ein <strong>Verhältnis</strong></td>
<td>Verhältnis-Bedingung (B12)</td>
</tr>
<tr class="even">
<td>„Jeder Auftrag genau einem Mitarbeiter.”</td>
<td>eine vollständige, eindeutige Zuordnung</td>
<td>Zuordnung 1:1 (B23)</td>
</tr>
<tr class="odd">
<td>„Eine Maschine kann nur eines gleichzeitig.”</td>
<td>zeitliche Ausschließlichkeit</td>
<td>Nichtüberlappung (B14)</td>
</tr>
<tr class="even">
<td>„B kann erst nach A.”</td>
<td>eine Reihenfolge</td>
<td>Vorrangbeziehung (B13)</td>
</tr>
<tr class="odd">
<td>„Höchstens fünf Tage am Stück.”</td>
<td>eine Bedingung über ein <strong>gleitendes Fenster</strong></td>
<td>gleitendes Fenster (B16)</td>
</tr>
<tr class="even">
<td>„Umstellen kostet uns eine halbe Stunde.”</td>
<td>reihenfolgeabhängige Kosten</td>
<td>Umrüstkosten (B17), Rüstzeit als Kapazität (B26)</td>
</tr>
<tr class="odd">
<td>„Was reinkommt, muss auch wieder raus.”</td>
<td>Erhaltung an jedem Knoten</td>
<td>Flusserhaltung (B22)</td>
</tr>
<tr class="even">
<td>„Notfalls brechen wir die Regel.”</td>
<td>die Bedingung ist weich, aber teuer</td>
<td>Schlupfvariablen (B18)</td>
</tr>
<tr class="odd">
<td>„Erst Termintreue, dann Kosten.”</td>
<td>eine Rangfolge zwischen Zielen</td>
<td>hierarchische Ziele (B19)</td>
</tr>
<tr class="even">
<td>„Es soll gerecht zugehen.”</td>
<td>den Schlechtestgestellten verbessern</td>
<td>Min/Max in der Zielfunktion (B11)</td>
</tr>
<tr class="odd">
<td>„Was brächte uns eine zusätzliche Stunde?”</td>
<td>die Frage nach dem Grenzwert einer Ressource</td>
<td>Schattenpreis (<a href="lp.html#kap-lp">Kapitel 5</a>, <a href="praxisfallen.html#sec:praxisfallen-attribution">Abschnitt 22.4</a>)</td>
</tr>
<tr class="even">
<td>„Mit 95 % Sicherheit muss es halten.”</td>
<td>eine Zusage auf eine <strong>Quote</strong>, nicht auf einen Mittelwert</td>
<td>Chance Constraint (<a href="unsicherheit.html#sec:unsicherheit-chance-constraints">Abschnitt 12.7</a>)</td>
</tr>
<tr class="odd">
<td>„Wir sichern uns gegen Schätzfehler ab.”</td>
<td>gegen die ungünstigste Parameterlage planen</td>
<td>Worst-Case-Abzug (B21)</td>
</tr>
</tbody>
</table>
<h3 id="fünf-wendungen-bei-denen-die-übersetzung-eine-entscheidung-erzwingt">Fünf Wendungen, bei denen die Übersetzung eine Entscheidung erzwingt</h3>
<p>Die Tabelle oben tut so, als sei die Zuordnung eindeutig. Bei den folgenden fünf ist sie es nicht — und <strong>das Nachfragen ist die eigentliche Modellierungsarbeit</strong>:</p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>Wendung</th>
<th>Die Frage, die dahinter steckt</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>„möglichst”</strong></td>
<td>Hart oder weich? Und wenn weich: Was darf ein Verstoß kosten? Ohne Zahl ist „möglichst” keine Bedingung, sondern eine Stimmung.</td>
</tr>
<tr class="even">
<td><strong>„nicht mehr als drei pro Woche”</strong></td>
<td>Je Person oder über alle zusammen? Kalenderwoche oder gleitende sieben Tage? Beide Lesarten sind vertretbar und ergeben verschiedene Modelle.</td>
</tr>
<tr class="odd">
<td><strong>„im Durchschnitt”</strong></td>
<td>Fast immer eine Falle. Der Durchschnittskunde kauft nie — siehe <a href="unsicherheit.html#sec:unsicherheit-der-fluch-des-durchschnitts">Abschnitt 12.3</a>. Gemeint ist meist ein Quantil („in 9 von 10 Fällen”).</td>
</tr>
<tr class="even">
<td><strong>„so schnell wie möglich”</strong></td>
<td>Welche Zahl? Ende des letzten Auftrags, Summe aller Fertigstellungszeiten oder größte Verspätung? Drei Zielfunktionen, drei verschiedene Pläne.</td>
</tr>
<tr class="odd">
<td><strong>„fair”</strong></td>
<td>Den Schlechtesten verbessern, die Spannweite verkleinern oder gleiche Anteile erzwingen? Erst die Wahl macht daraus ein Modell — und sie gehört dem Betrieb, nicht dem Modellierer.</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>🎯 Der Nutzen dieser Liste</strong> Sie ersetzt kein Nachdenken. Sie sorgt dafür, dass das Nachdenken an den <strong>richtigen</strong> vier oder fünf Stellen stattfindet, statt an allen zwanzig gleichzeitig.</p>
</blockquote>
<h3 id="und-die-gegenrichtung-or-deutsch">Und die Gegenrichtung: OR — Deutsch</h3>
<p>Dieselbe Barriere gibt es rückwärts. Diese Sätze haben sich bewährt, wenn ein Ergebnis jemandem erklärt werden muss, der das Modell nicht kennt:</p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>Fachbegriff</th>
<th>Was Sie in der Besprechung sagen</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Schattenpreis / Dualwert</td>
<td>„Die nächste Stunde auf dieser Maschine wäre uns 9 € wert.”</td>
</tr>
<tr class="even">
<td>bindende Bedingung mit Dualwert 0</td>
<td>„Die Regel berührt den Plan, kostet uns aber nichts — dort zu verhandeln bringt nichts.”</td>
</tr>
<tr class="odd">
<td><code>INFEASIBLE</code></td>
<td>„Ihre Regeln widersprechen sich.” <strong>Nicht:</strong> „Der Computer schafft es nicht.”</td>
</tr>
<tr class="even">
<td>Optimalitätslücke von 2 %</td>
<td>„Besser als das hier geht es höchstens um 2 % — und die liegen unter unserer Datenungenauigkeit.”</td>
</tr>
<tr class="odd">
<td>CVaR</td>
<td>„Wenn es schiefgeht, verlieren wir im Mittel so viel.” (Der VaR sagt nur, <strong>ob</strong> es schiefgeht.)</td>
</tr>
<tr class="even">
<td>EVPI</td>
<td>„Selbst eine perfekte Prognose wäre uns höchstens so viel wert.”</td>
</tr>
<tr class="odd">
<td>Relaxation</td>
<td>„Wir haben die Regel probeweise gelockert, um zu sehen, was sie kostet.”</td>
</tr>
<tr class="even">
<td>Chance Constraint</td>
<td>„Der Plan hält in 95 von 100 Fällen — und der nächste Prozentpunkt kostet …”</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="sec:modellierung-uebungsaufgaben">4.7 Übungsaufgaben</h2>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<blockquote>
<p>Lösungen: <a href="anhang-loesungen.html#sec:loesungen-modellierung">Abschnitt A.4</a>.</p>
</blockquote>
<p><strong>Aufgabe 4.1 ⭐ — Die fehlende Lesart.</strong> Der Text nennt zwei Lesarten von „Stammkunden dürfen wir nicht verlieren”. Formulieren Sie eine dritte, die weder A noch B ist, und sagen Sie, welchen Betrieb sie beschreiben würde.</p>
<p><strong>Aufgabe 4.2 ⭐ — Frage 5 anwenden.</strong> Ein Krankenhaus möchte „die Wartezeiten in der Notaufnahme senken”. Wenden Sie Frage 5 an: Welcher Plan käme heraus, wenn genau die durchschnittliche Wartezeit minimiert würde — und was wäre daran nicht akzeptabel?</p>
<p><strong>Aufgabe 4.3 ⭐⭐ — Der Kompromiss dazwischen.</strong> Zwischen Lesart A (12 500 €) und Lesart B (3 500 €) liegt Spielraum. Bauen Sie eine Variante, in der jeder Stammkunde <strong>mindestens die Hälfte</strong> seiner Auftragsstunden zugeteilt bekommt. Was kostet sie?</p>
<p><strong>Aufgabe 4.4 ⭐⭐ — Zwei Ziele gleichzeitig.</strong> Maximieren Sie <span class="math inline">\text{DB} + \lambda \cdot \text{Umsatz}</span> für <span class="math inline">\lambda \in \{0; 0{,}1; 0{,}3; 1\}</span> und tragen Sie die Ergebnisse als Tabelle auf. Ab welchem <span class="math inline">\lambda</span> kippt der Plan von der DB- in die Umsatzlösung? Was sagt dieser Wert dem Betrieb?</p>
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<p><strong>Aufgabe 4.5 ⭐⭐ — Übersetzen, wo es nicht eindeutig ist.</strong> Übersetzen Sie mit <a href="#sec:modellierung-glossar">Abschnitt 4.6</a>: (a) „Die Monteure sollen möglichst gleichmäßig ausgelastet sein.” Nennen Sie <strong>drei</strong> Zielfunktionen, die alle „gleichmäßig” bedeuten können, und je einen Fall, in dem die Wahl den Plan sichtbar ändert. (b) „Kein Kunde darf länger als drei Tage warten.” Formulieren Sie die Bedingung einmal hart und einmal weich. Welche Frage müssen Sie dem Betrieb stellen, um zu entscheiden? (c) Suchen Sie in einer echten Anforderung aus Ihrem Umfeld einen Satz, der nach der Tabelle eindeutig aussieht, es aber nicht ist.</p>
<p><strong>Aufgabe 4.6 ⭐⭐⭐ — Die Anforderung schreiben.</strong> Formulieren Sie für den Fall dieses Kapitels eine vollständige, prüfbare Anforderung: Ziel, harte Regeln, weiche Regeln mit Strafgewichten, Verhalten bei Unlösbarkeit, auszuweisende Kennzahlen. Zwei Seiten genügen — sie sind mehr wert als zwei Wochen Programmierung.</p>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<hr />
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<h2 id="sec:modellierung-denkfehler">4.8 Finde den Denkfehler</h2>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<div class="card card-denkfehler">
<blockquote>
<p><strong>🐛 „Die Engpassmaschine soll zu mindestens 92 % ausgelastet sein”</strong></p>
<p>Der Werkleiter formuliert eine klare, messbare, prüfbare Anforderung:</p>
<blockquote>
<p><em>„Die Engpassmaschine soll in jedem Planungszeitraum zu mindestens 92 % ausgelastet sein. &gt; Das ist unsere teuerste Anlage, Leerlauf können wir uns nicht leisten.”</em></p>
</blockquote>
<p>Der Entwickler baut sie als harte Nebenbedingung ein — sie ist ja klar formuliert. Die Abnahme läuft mit den Daten des letzten Quartals: Der Plan hält die Vorgabe ein, der Deckungsbeitrag stimmt, alle Tests grün. Das System geht in Betrieb.</p>
<p><strong>Diese Anforderung enthält zwei verschiedene Fehler. Welche?</strong></p>
<p>Ein Hinweis für den zweiten: Sehen Sie sich an, was das Modell im Abnahmetest <em>tatsächlich</em> geprüft hat — und rechnen Sie einen Monat durch, in dem weniger Anfragen hereinkommen.</p>
</blockquote>
</div>
<hr />
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<h2 id="sec:modellierung-quiz">4.9 Micro-Quiz</h2>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<div class="card card-quiz">
<blockquote>
<p><strong>❓ Drei Fragen</strong></p>
<p><strong>1. Warum liefert „Umsatz maximieren” hier einen schlechteren Plan als „Deckungsbeitrag maximieren”, obwohl beide Modelle korrekt sind?</strong> a) Weil der Solver bei Umsatzzielen ungenauer rechnet. b) Weil alles, was nicht in der Zielfunktion steht, für das Modell wertlos ist — nicht weniger wichtig. c) Weil Umsatzdaten in der Praxis unzuverlässiger sind als Kostendaten.</p>
<p><strong>2. Eine Regel als harte Nebenbedingung statt als Strafkosten zu formulieren, ist vor allem deshalb riskant, weil …</strong> a) … harte Nebenbedingungen den Solver verlangsamen. b) … das Modell unlösbar wird, sobald die Regel einmal nicht erfüllbar ist — und dann gar keine Antwort mehr liefert. c) … sich harte Regeln nachträglich nicht mehr ändern lassen.</p>
<p><strong>3. Alle drei Zielgrößen erreichen 100 % Auslastung. Was folgt daraus?</strong> a) Die Auslastung war schon vorher optimal, das Projekt ist überflüssig. b) Bei knapper Kapazität ist volle Auslastung die Voreinstellung; zu entscheiden ist nur, <em>womit</em> die Maschine voll wird. c) Die Kapazitätsdaten sind vermutlich falsch erfasst.</p>
</blockquote>
</div>
<hr />
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<h2 id="sec:modellierung-selbsttest">4.10 Selbsttest</h2>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<ol type="1">
<li>Warum ist „Was ist Ihnen wichtig?” eine schlechte Frage und „Welche Zahl steht im Jahresbericht?” eine gute?</li>
<li>Nennen Sie zwei Kennzahlen aus Ihrem eigenen Arbeitsumfeld, deren Maximierung schaden würde.</li>
<li>Erklären Sie in einem Satz, warum eine harte Regel eine weiche mit großer Strafe ist — und wann der Unterschied trotzdem zählt.</li>
<li>Ein Kunde sagt: „Kein Mitarbeiter soll mehr als zwei Wochenenden im Monat arbeiten.” Nennen Sie zwei Lesarten und je eine Situation, in der sie auseinanderfallen.</li>
<li>Was antworten Sie auf die Frage „Was soll passieren, wenn es keine zulässige Lösung gibt?” — und warum ist „das kommt nicht vor” keine Antwort?</li>
</ol>
<hr />
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<h2 id="sec:modellierung-zusammenfassung">4.11 Zusammenfassung</h2>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<ul>
<li>Ein Modell rechnet aus, was <strong>aufgeschrieben</strong> wurde, nicht was gemeint war. Die Zielfunktion ist eine vollständige Aussage: Was nicht darin steht, ist wertlos.</li>
<li>Die <strong>Zielgröße</strong> ist die folgenreichste Entscheidung eines OR-Projekts. Umsatz, Auslastung und Deckungsbeitrag führten hier auf demselben Datensatz zu drei verschiedenen Plänen mit 24 500 € Unterschied.</li>
<li><strong>Auslastung ist ein Ergebnis, kein Ziel.</strong> Bei knapper Kapazität ist die Maschine ohnehin voll; entscheidend ist, womit.</li>
<li>Ein Satz aus einer Besprechung hat meist <strong>mehrere Lesarten</strong>. „Stammkunden nicht verlieren” kostete je nach Lesart 3 500 € oder 12 500 € — bei identischem Ergebnis für die Kunden.</li>
<li><strong>Hart ist nur, was rechtlich oder physikalisch unmöglich ist.</strong> Eine harte Regel ist eine weiche mit hinreichend großer Strafe; der Unterschied zeigt sich erst, wenn sie einmal nicht erfüllbar ist — dann liefert die harte Variante gar nichts mehr.</li>
Phase 7.3: Glossar "Deutsch - OR" - und die Gegenrichtung Setzt Paket 7 aus Verbesserungen_03.md um. Neuer Abschnitt im Kapitel "Vom Management-Wunsch zum Modell", unmittelbar hinter dem Gespraechsleitfaden: Die zwoelf Fragen liefern Saetze - hier werden sie uebersetzt. Die Abgrenzung zu Anhang B war die eigentliche Entwurfsfrage. Der Musterkatalog hat laengst eine Spalte "Umgangssprachlich", laeuft aber in die andere Richtung: Muster -> Formulierung. Wer aus einer Besprechung kommt, hat den Satz und sucht den Baustein. Das Lexikon ist genau so herum gebaut (21 Eintraege, jeweils mit B-Nummer), und Anhang B bekam einen Rueckverweis. Kein Duplikat, sondern die fehlende Gegenrichtung. Der Teil, der den Abschnitt traegt, ist aber nicht die Tabelle, sondern was danach kommt: fuenf Wendungen, bei denen die Uebersetzung eine Entscheidung erzwingt - "moeglichst", "nicht mehr als drei pro Woche", "im Durchschnitt", "so schnell wie moeglich", "fair". Bei jeder gibt es zwei bis drei vertretbare Modelle, und die Wahl gehoert dem Betrieb, nicht dem Modellierer. Das ist die These des Kapitels, auf die Alltagssprache angewandt. Dazu die Gegenrichtung OR -> Deutsch: acht Saetze fuer die Erklaerung eines Ergebnisses, einschliesslich der Formulierung fuer INFEASIBLE ("Ihre Regeln widersprechen sich", nicht "der Computer schafft es nicht"). Die neue Uebungsaufgabe laesst drei Zielfunktionen fuer "gleichmaessig ausgelastet" aufstellen; die Loesung nennt je einen Fall, in dem die Wahl den Plan sichtbar aendert - und weist darauf hin, dass zwei der drei einen Halbtagsmonteur bestrafen. Mitgezogen: Lernziel, Zusammenfassung, Rueckverweis aus Anhang B, Pfad E im Vorwort (er fuehrte bisher direkt zum Musterkatalog, jetzt ueber das Lexikon). Damit ist Phase 7 abgeschlossen. 296 Abschnitte, 766 Querverweise, 141 Aufgaben mit 141 Loesungen, PDF 749 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:45:42 +02:00
<li><strong>Die meisten Sätze haben eine Standardübersetzung</strong> — das Lexikon in <a href="#sec:modellierung-glossar">Abschnitt 4.6</a> nennt sie. Sein Wert liegt aber in den fünf Wendungen, bei denen es keine gibt: „möglichst”, „nicht mehr als”, „im Durchschnitt”, „so schnell wie möglich”, „fair”. Dort ist Nachfragen die Modellierungsarbeit.</li>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
<li>Die vier Fragen — worüber wird entschieden, woran gemessen, was ist unverhandelbar, was bei Unlösbarkeit — gehören in die erste Besprechung. Der Leitfaden in <a href="#sec:modellierung-leitfaden">Abschnitt 4.5</a> macht zwölf daraus.</li>
</ul>
</article>
<button type="button" class="fortschritt-knopf" data-kapitel="modellierung.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
Phase 8.1: Synthese-Seiten je Teil - und die gebrochene Lesekette Setzt Paket 5 aus Verbesserungen_02.md um (den Teil, der nicht zurueckgestellt wurde). Fuenf neue Dateien 19_/29_/39_/49_/52_Synthese_*.md, je eine am Ende eines Teils, mit eigener Website-Seite ueber SONDERSEITEN - sie tragen bewusst keine "# Kapitel:"-Ueberschrift, weil sie keine Kapitel sind, sondern der Rueckblick auf einen Teil. Der Entwurf musste sich abgrenzen: Die Teil-Einleitungen haben bereits Entscheidungsdiagramme. Eine zweite Matrix am Teil-Ende waere eine Dopplung gewesen. Die Synthesen leisten deshalb, was eine Einleitung nicht kann - den Vergleich ueber die Kapitel hinweg (Verfahren nebeneinander, mit der Spalte "wo es aufhoert"), eine Tabelle "was dieser Teil gemessen hat" (Behauptung gegen Messung gegen Fundstelle) und drei Fehler, die der Teil verhindert. Zitiert wird ausschliesslich, was im Buch tatsaechlich gerechnet wird. Drei Funde beim Einbau: * Teil III sagte "die drei Kapitel dieses Teils", hat aber fuenf. Phase 3 hatte Mehrziel und Predict-then-Optimize hinzugefuegt, die Einleitung blieb stehen. * 50_Praxis.md verwies auf die Projektwerkstatt mit "acht eigene Anwendungen" - sie hat elf. * Und der eigentliche Fund: Die Lesekette der Quelldateien fuehrte an ACHT Kapiteln vorbei. 12_Python_Oekosystem zeigte direkt auf 20_Lineare_Programmierung, 23_Graphen direkt auf 30_QP, 32_Dynamische direkt auf 40_Finanzdaten, 50_Praxis direkt auf die Projektwerkstatt. Wer der Kette folgte, uebersprang acht von 23 Kapiteln - darunter Metaheuristiken, Spaltengenerierung, Strukturbruecke, Supply-Chain und das ganze Testing-Kapitel. Zehn weitere Dateien hatten gar keine Navigationszeile. Zur Reichweite, damit sie nicht ueberschaetzt wird: Diese Zeilen stehen nur in den Quelldateien. entferne_navigation() streicht sie aus dem Gesamtdokument, und die Website baut ihre Vor/Zurueck-Knoepfe selbst aus DATEIEN. PDF und Website waren nie betroffen - wohl aber jeder, der die Markdown-Dateien im Repository liest, und das wird nach der Veroeffentlichung der Normalfall sein. Die Kette ist jetzt ueber alle 35 Uebergaenge geschlossen, und --check bewacht sie: Fehlt eine Zeile oder zeigt sie an der in DATEIEN folgenden Datei vorbei, ist der Lauf rot. Gegengetestet mit beiden Bruchformen. Stand: 36 Dateien, 296 Abschnitte, 815 Querverweise, 328 Indexmarken, 76 Programme (unveraendert), 33 pytest-Tests, PDF 758 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 12:10:02 +02:00
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="oekosystem.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a><a class="prev-next-knopf prev-next-next" href="synthese-grundlagen.html"><span><small>Weiter</small>Synthese Teil I</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
Version 04 als eigenes Repository Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
</main>
</div>
<footer class="site-footer">
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
<a href="programme.html">Beispielprogramme</a></p>
</footer>
<script defer src="assets/search-index.js"></script>
<script defer src="assets/programme.js"></script>
<script defer src="assets/site.js"></script>
</body>
</html>