<title>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung · Optimierte Entscheidungsfindung mit Python</title>
<script>
(function () {
try {
var t = localStorage.getItem("or-theme");
if (t) document.documentElement.setAttribute("data-theme", t);
<aclass="brand"href="index.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-book"></use></svg><span>Optimierte Entscheidungsfindung mit Python</span></a>
<navclass="sidebar"id="sidebar"aria-label="Kapitelnavigation"><divclass="sidebar-inhalt"><detailsclass="sidebar-gruppe"><summary>Einstieg</summary><ul><lidata-kapitel="vorwort.html"><ahref="vorwort.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><lidata-kapitel="notation.html"><ahref="notation.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><detailsclass="sidebar-gruppe"open><summary>Teil I: Grundlagen des Operations Research</summary><ul><lidata-kapitel="einfuehrung.html"class="aktiv"><ahref="einfuehrung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><lidata-kapitel="fundament.html"><ahref="fundament.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><lidata-kapitel="oekosystem.html"><ahref="oekosystem.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><lidata-kapitel="modellierung.html"><ahref="modellierung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><lidata-kapitel="lp.html"><ahref="lp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><lidata-kapitel="milp.html"><ahref="milp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><lidata-kapitel="cpsat.html"><ahref="cpsat.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><lidata-kapitel="graphen.html"><ahref="graphen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><lidata-kapitel="metaheuristiken.html"><ahref="metaheuristiken.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><lidata-kapitel="dekomposition.html"><ahref="dekomposition.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</summary><ul><lidata-kapitel="qp-nlp.html"><ahref="qp-nlp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a></li><lidata-kapitel="unsicherheit.html"><ahref="unsicherheit.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-ch
<mainclass="content">
<navclass="breadcrumb"aria-label="Breadcrumb"><ahref="index.html">Start</a>›<span>Teil I</span>›<span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></nav>
<h1id="teil-grundlagen">Teil I: Grundlagen des Operations Research</h1>
<hr/>
<h1id="kap-einfuehrung">Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</h1>
<divclass="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Was Operations Research ist, warum Ausprobieren ab einer gewissen Problemgröße hoffnungslos wird, und aus welchen vier Bausteinen <em>jedes</em> Optimierungsmodell besteht. Los geht es mit einem Problem, das Sie in fünf Minuten selbst lösen.</p>
<p><strong>Voraussetzungen:</strong> Keine. Dies ist der Einstieg.</p>
<p><strong>Danach können Sie:</strong> Ein Alltagsproblem in Entscheidungsvariablen, Parameter, Zielfunktion und Nebenbedingungen zerlegen, ein erstes Modell mit Google OR-Tools lösen — und die Daten dafür aus einer Excel-Mappe holen und das Ergebnis dorthin zurückschreiben.</p>
<p><strong>Zeitbedarf:</strong> ca. 3,5 Stunden inklusive Übungen.</p>
<ahref="https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/einfuehrung.ipynb">In Google Colab öffnen</a></p>
</blockquote>
</div>
<hr/>
<h2id="sec:einfuehrung-schnellstart">1.1 In 5 Minuten gelöst</h2>
<p>Bevor wir über Theorie sprechen, lösen Sie ein echtes Problem. Kopieren Sie den folgenden Block, führen Sie ihn aus, sehen Sie das Ergebnis — die Erklärung kommt danach.</p>
<divclass="card card-schnellstart">
<blockquote>
<p><strong>🚀 In 5 Minuten gelöst: Der Wochenplan einer Schreinerei</strong></p>
<p>Eine Schreinerei fertigt Tische und Stühle. Nächste Woche stehen <strong>150 Montagestunden</strong> und <strong>240 m² Plattenmaterial</strong> zur Verfügung. Die Frage des Chefs: <em>Was sollen wir bauen?</em></p>
<p><strong>Und jetzt der Punkt, auf den es ankommt.</strong> Fragen Sie sich ehrlich: Was hätten <em>Sie</em> geantwortet? Die naheliegende Antwort lautet „möglichst viele Tische“ — der Tisch bringt mit 240 € schließlich den vierfachen Deckungsbeitrag eines Stuhls. Rechnen wir das durch:</p>
<p>Das Bauchgefühl kostet <strong>1 200 € pro Woche</strong>, rund 62 000 € im Jahr — bei einem Problem mit sage und schreibe zwei Produkten und zwei Ressourcen. Genau das ist Operations Research: nicht das Ausrechnen von Zahlen, die man ohnehin kennt, sondern das Auffinden von Entscheidungen, auf die man von allein nicht kommt.</p>
<p><strong>Warum funktioniert das?</strong> Der Solver macht sich zunutze, dass keines der beiden Produkte in <em>beiden</em> Ressourcen das bessere ist. Pro Montagestunde bringt der Tisch 80 €, der Stuhl nur 60 € — pro Quadratmeter Plattenmaterial dagegen bringt der Stuhl 60 €, der Tisch nur 40 €. Wo kein Produkt durchgehend gewinnt, gibt es eine Mischung, die beide Engpässe gleichzeitig ausschöpft. Diese Mischung von Hand zu finden ist schon bei zwei Produkten mühsam und bei zwanzig unmöglich. Wie der Solver sie findet, zeigt <ahref="lp.html#kap-lp">Kapitel 5</a>; was die einzelnen Zeilen des Programms bedeuten, klärt der Rest dieses Kapitels.</p>
<li>… in eigenen Worten erklären, wodurch sich Operations Research von Business Intelligence und maschinellem Lernen unterscheidet.</li>
<li>… abschätzen, ab welcher Problemgröße vollständiges Durchprobieren scheitert — und diese Abschätzung selbst rechnen.</li>
<li>… die vier Bausteine eines Modells benennen und einem konkreten Problem zuordnen.</li>
<li>… entscheiden, ob eine Bedingung eine <strong>harte</strong> oder eine <strong>weiche</strong> Nebenbedingung ist.</li>
<li>… ein einfaches Modell mit <strong>Google OR-Tools</strong> aufschreiben, lösen und das Ergebnis interpretieren.</li>
</ol>
<hr/>
<h2id="sec:einfuehrung-was-ist-operations-research-wirklich">1.3 Was ist Operations Research wirklich?</h2>
<p>Im Alltag lösen wir Probleme intuitiv: durch Ausprobieren, Daumenregeln, Erfahrung. Das funktioniert erstaunlich gut — bis eine bestimmte Schwelle überschritten wird. Dann bricht die Intuition nicht langsam ein, sondern schlagartig zusammen.</p>
<p>Drei Beispiele:</p>
<ul>
<li>Ein Disponent soll 40 Lastwagen auf 300 Lieferorte verteilen. Es gibt mehr mögliche Routenkombinationen als Atome im bekannten Universum: <spanclass="math inline">300! \approx 3{,}06 \times 10^{614}</span>.</li>
<li>Eine Klinik muss 200 Pflegekräfte einteilen — unter Berücksichtigung von 15 gesetzlichen Arbeitszeitregeln, Urlaubsanträgen, Qualifikationsstufen und Notfallreserven.</li>
<li>Ein Fondsmanager muss 10 Millionen Euro so auf 50 Aktien aufteilen, dass bei einer erwarteten Mindestrendite von 8 % das Verlustrisiko im schlechtesten Marktszenario minimal bleibt.</li>
</ul>
<p>Ein naiver Ansatz — <em>Brute Force</em>, das vollständige Durchrechnen aller Möglichkeiten — würde selbst auf den schnellsten Supercomputern der Welt Milliarden Jahre dauern.</p>
<p><strong>Operations Research (OR)</strong>, deutsch etwa <em>Unternehmensforschung</em> oder <em>mathematische Entscheidungsoptimierung</em>, ist die wissenschaftliche Disziplin, reale Entscheidungsprobleme in formale mathematische Modelle zu überführen und mithilfe exakter oder heuristischer Algorithmen (<em>Solver</em>) die beweisbar beste (<strong>optimale</strong>) oder eine messbar hochwertige Lösung zu finden.</p>
</blockquote>
<h3id="die-drei-stufen-der-analytik">Die drei Stufen der Analytik</h3>
<p>OR beantwortet nicht die beschreibende Frage <em>„Was ist passiert?“</em> und nicht die vorhersagende Frage <em>„Was wird passieren?“</em>, sondern die <strong>vorschreibende</strong> (präskriptive) Kernfrage:</p>
<p><spanclass="math display">
\textbf{„Was ist unter den gegebenen Bedingungen die beste Handlung?“}
</span></p>
<figure>
<imgsrc="bilder_04/kap01_analytik_reifegrad.svg"alt="Abb. 1.1: Analytik-Reifegrad – vom Beschreiben zum Entscheiden"/>
<figcaptionaria-hidden="true">Abb. 1.1: Analytik-Reifegrad – vom Beschreiben zum Entscheiden</figcaption>
</figure>
<table>
<colgroup>
<colstyle="width: 15%"/>
<colstyle="width: 20%"/>
<colstyle="width: 35%"/>
<colstyle="width: 28%"/>
</colgroup>
<thead>
<trclass="header">
<th>Stufe</th>
<th>Frage</th>
<th>Typisches Werkzeug</th>
<th>Ergebnis</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><strong>Deskriptiv</strong></td>
<td>Was ist passiert?</td>
<td>Business Intelligence, Dashboards</td>
<td>Ein Bericht</td>
</tr>
<trclass="even">
<td><strong>Prädiktiv</strong></td>
<td>Was wird geschehen?</td>
<td>Statistik, maschinelles Lernen</td>
<td>Eine Prognose</td>
</tr>
<trclass="odd">
<td><strong>Präskriptiv</strong></td>
<td>Was sollen wir tun?</td>
<td><strong>Operations Research</strong></td>
<td>Eine <strong>Handlungsanweisung</strong></td>
</tr>
</tbody>
</table>
<p>Die Stufen bauen aufeinander auf, ersetzen einander aber nicht. Eine Umsatzprognose sagt Ihnen, dass die Nachfrage im März um 20 % steigt. Sie sagt Ihnen nicht, welche der sieben Maschinen Sie dafür wann mit welchem Auftrag belegen sollen. Genau dort beginnt OR — und deshalb ist eine Prognose in diesem Buch fast immer nur eine <strong>Eingangsgröße</strong> des Modells, nie sein Ergebnis.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Maschinelles Lernen sagt Ihnen, was kommt. Operations Research sagt Ihnen, was Sie deswegen tun sollen.</p>
</blockquote>
<hr/>
<h2id="sec:einfuehrung-warum-ausprobieren-scheitert-mit-eigener-rechnung">1.4 Warum Ausprobieren scheitert — mit eigener Rechnung</h2>
<p>Die Behauptung „vollständiges Durchprobieren ist unmöglich“ wird oft aufgestellt und selten belegt. Rechnen wir sie einmal selbst nach — das Ergebnis prägt sich besser ein als jede Zahl im Text.</p>
<blockquote>
<p><strong>✏️ Handrechnung 1.1: Wie schnell wächst „alle Möglichkeiten“?</strong></p>
<p>Angenommen, Sie sollen <spanclass="math inline">n</span> Aufträge in eine Reihenfolge bringen. Für den ersten Platz haben Sie <spanclass="math inline">n</span> Kandidaten, für den zweiten <spanclass="math inline">n-1</span>, für den dritten <spanclass="math inline">n-2</span> und so fort. Die Gesamtzahl der Reihenfolgen ist damit die <strong>Fakultät</strong>:</p>
<p>Nehmen wir großzügig an, ein Rechner prüfe <strong>eine Milliarde</strong> (<spanclass="math inline">10^9</span>) Reihenfolgen pro Sekunde. Wie lange dauert es?</p>
<table>
<thead>
<trclass="header">
<th><spanclass="math inline">n</span></th>
<th><spanclass="math inline">n!</span></th>
<th>Rechenzeit bei <spanclass="math inline">10^9</span> Prüfungen/s</th>
<p><strong>Der springende Punkt:</strong> Zwischen <spanclass="math inline">n = 15</span> (22 Minuten, machbar) und <spanclass="math inline">n = 20</span> (77 Jahre, hoffnungslos) liegen nur <strong>fünf zusätzliche Aufträge</strong>. Es gibt keine sanfte Verschlechterung — es gibt eine Wand.</p>
</blockquote>
<p>Und die Rechnung ist noch geschönt: Ein schnellerer Rechner hilft praktisch nicht. Selbst eine Beschleunigung um den Faktor <strong>eine Million</strong> verschiebt die Grenze von <spanclass="math inline">n=20</span> nur auf etwa <spanclass="math inline">n=24</span>. Gegen kombinatorisches Wachstum ist Hardware machtlos.</p>
<p>Das folgende Programm macht diesen Effekt erfahrbar. Es löst dasselbe Zuordnungsproblem zweimal — einmal durch vollständiges Durchprobieren, einmal mit einem OR-Solver — und vergleicht die Laufzeiten.</p>
<spanid="cb3-6"><ahref="#cb3-6"aria-hidden="true"tabindex="-1"></a><spanclass="co">Vergleicht vollständige Enumeration mit einem Constraint-Solver an einem</span></span>
<spanid="cb3-7"><ahref="#cb3-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">Zuordnungsproblem wachsender Größe (n Mitarbeiter auf n Aufgaben).</span></span>
<spanid="cb3-19"><ahref="#cb3-19"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Zufällige, aber reproduzierbare Kosten: Wer bearbeitet welche Aufgabe wie teuer?"""</span></span>
<spanid="cb3-26"><ahref="#cb3-26"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Probiert ALLE n! Zuordnungen durch und behält die beste.</span></span>
<spanid="cb3-27"><ahref="#cb3-27"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Rückgabe: (bester Kostenwert, beste Permutation, Anzahl geprüfter Kombinationen)</span></span>
<spanid="cb3-36"><ahref="#cb3-36"aria-hidden="true"tabindex="-1"></a><spanclass="co"># zuordnung[i] = Aufgabe, die Mitarbeiter i übernimmt</span></span>
<spanid="cb3-37"><ahref="#cb3-37"aria-hidden="true"tabindex="-1"></a> wert <spanclass="op">=</span><spanclass="bu">sum</span>(kosten[i][zuordnung[i]] <spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">range</span>(n))</span>
<spanid="cb3-47"><ahref="#cb3-47"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Dasselbe Problem als Constraint-Programm — der Solver probiert NICHT alles durch."""</span></span>
<spanid="cb3-48"><ahref="#cb3-48"aria-hidden="true"tabindex="-1"></a> n <spanclass="op">=</span><spanclass="bu">len</span>(kosten)</span>
<spanid="cb3-49"><ahref="#cb3-49"aria-hidden="true"tabindex="-1"></a> modell <spanclass="op">=</span> cp_model.CpModel()</span>
<spanid="cb3-51"><ahref="#cb3-51"aria-hidden="true"tabindex="-1"></a><spanclass="co"># x[i][j] = 1 <=> Mitarbeiter i übernimmt Aufgabe j</span></span>
<spanid="cb3-52"><ahref="#cb3-52"aria-hidden="true"tabindex="-1"></a> x <spanclass="op">=</span> [[modell.NewBoolVar(<spanclass="ss">f"x_</span><spanclass="sc">{</span>i<spanclass="sc">}</span><spanclass="ss">_</span><spanclass="sc">{</span>j<spanclass="sc">}</span><spanclass="ss">"</span>) <spanclass="cf">for</span> j <spanclass="kw">in</span><spanclass="bu">range</span>(n)] <spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">range</span>(n)]</span>
<spanid="cb3-54"><ahref="#cb3-54"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">range</span>(n):</span>
<spanid="cb3-55"><ahref="#cb3-55"aria-hidden="true"tabindex="-1"></a> modell.AddExactlyOne(x[i][j] <spanclass="cf">for</span> j <spanclass="kw">in</span><spanclass="bu">range</span>(n)) <spanclass="co"># jeder genau eine Aufgabe</span></span>
<spanid="cb3-63"><ahref="#cb3-63"aria-hidden="true"tabindex="-1"></a> status <spanclass="op">=</span> loeser.Solve(modell)</span>
<spanid="cb3-64"><ahref="#cb3-64"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> status <spanclass="kw">not</span><spanclass="kw">in</span> (cp_model.OPTIMAL, cp_model.FEASIBLE):</span>
<spanid="cb3-65"><ahref="#cb3-65"aria-hidden="true"tabindex="-1"></a><spanclass="cf">raise</span><spanclass="pp">RuntimeError</span>(<spanclass="st">"Solver fand keine Lösung."</span>)</span>
<spanid="cb3-95"><ahref="#cb3-95"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Hochrechnung: Wie lange bräuchte Brute Force bei n = 20?</span></span>
<spanid="cb3-97"><ahref="#cb3-97"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Messbasis: Prüfungen pro Sekunde aus dem letzten Lauf schätzen</span></span>
<spanid="cb3-99"><ahref="#cb3-99"aria-hidden="true"tabindex="-1"></a> jahre <spanclass="op">=</span> kombis_20 <spanclass="op">/</span> pro_sekunde <spanclass="op">/</span> (<spanclass="dv">60</span><spanclass="op">*</span><spanclass="dv">60</span><spanclass="op">*</span><spanclass="dv">24</span><spanclass="op">*</span><spanclass="fl">365.25</span>)</span>
<spanid="cb3-100"><ahref="#cb3-100"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Hochrechnung für n = 20: </span><spanclass="sc">{</span>kombis_20<spanclass="sc">:,}</span><spanclass="ss"> Kombinationen"</span>)</span>
<spanid="cb3-101"><ahref="#cb3-101"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Bei gemessenen </span><spanclass="sc">{</span>pro_sekunde<spanclass="sc">:,.0f}</span><spanclass="ss"> Prüfungen/s wären das </span><spanclass="sc">{</span>jahre<spanclass="sc">:,.0f}</span><spanclass="ss"> Jahre."</span>)</span>
<spanid="cb3-102"><ahref="#cb3-102"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Der Solver löst dieselbe Instanz in Sekundenbruchteilen."</span>)</span>
<p>Lesen Sie die Tabelle von oben nach unten: Bei <spanclass="math inline">n = 4</span> ist das Durchprobieren noch <strong>400-mal schneller</strong> als der Solver — der Solveraufruf hat einen festen Startaufwand von einigen Millisekunden, der sich bei winzigen Problemen nicht lohnt. Ab <spanclass="math inline">n = 8</span> kippt das Verhältnis, und ab <spanclass="math inline">n = 10</span> ist der Solver bereits <strong>480-mal schneller</strong>. Entscheidend ist nicht der Faktor, sondern die <em>Richtung</em>: Die Brute-Force-Zeit verzehnfacht sich mit jedem zusätzlichen Element, die Solver-Zeit wächst kaum merklich.</p>
<td>er nutzt Struktur (Schranken, Propagation), statt abzuzählen</td>
</tr>
</tbody>
</table>
<p><strong>Die entscheidende Beobachtung:</strong> Beide Programme liefern denselben Zielwert (Spalte „gleich?“). Der Solver rät also nicht und liefert keine Näherung — er findet dasselbe beweisbare Optimum, nur ohne alles anzusehen.</p>
</blockquote>
<blockquote>
<p><strong>⚠️ Typische Fehler</strong></p>
<ul>
<li><strong>„Dann nehme ich eben eine schnellere Sprache.“</strong> C++ ist etwa 50-mal schneller als Python. Bei <spanclass="math inline">n=20</span> verkürzt das 157 Millionen Jahre auf 3 Millionen Jahre. Das Problem ist nicht die Sprache, sondern der Ansatz.</li>
<li><strong>„Ich probiere nur die plausiblen Kombinationen.“</strong> Genau das tut ein Solver — nur systematisch, mit einem Beweis, dass die weggelassenen Zweige nichts Besseres enthalten. Eine handgestrickte Auswahl hat diesen Beweis nicht.</li>
</ul>
</blockquote>
<hr/>
<h2id="sec:einfuehrung-historischer-kontext-und-evolution">1.5 Historischer Kontext und Evolution</h2>
<p>Der Begriff entstand im Vorfeld des Zweiten Weltkriegs in Großbritannien. Militärische Führungsstäbe standen vor neuartigen logistischen und strategischen Fragen: Wie platziert man die neu entwickelten Radaranlagen an der Küste optimal? Wie groß müssen Schiffskonvois sein, um U-Boot-Angriffe bei minimalem Geleitschutzaufwand abzuwehren?</p>
<p>Wissenschaftler wie <strong>George Dantzig</strong>, <strong>Patrick Blackett</strong> und <strong>John von Neumann</strong> entwickelten daraufhin mathematische Formalismen. 1947 erfand George Dantzig den <strong>Simplex-Algorithmus</strong> zur Lösung linearer Programme — ein Durchbruch, der die industrielle Planung in den 1950er-Jahren revolutionierte und den wir in <ahref="lp.html#kap-lp">Kapitel 5</a> selbst programmieren werden.</p>
<p>Heute treibt OR die Kernsysteme moderner Industrien an:</p>
<ul>
<li><strong>Tech-Konzerne:</strong> Server-Scheduling, Datenrouting in Glasfasernetzen, Werbeplatzierungsauktionen.</li>
<li><strong>Luftfahrt & Logistik:</strong> Crew-Scheduling, Flugzeugumlaufplanung, Paketlogistik (etwa das System <em>ORION</em> von UPS, das durch Routenoptimierung jährlich zweistellige Millionenbeträge an Treibstoff einspart).</li>
<li><strong>Finanzindustrie:</strong> Arbitrage-Freiheit, Portfolio-Optimierung, Liquiditäts- und Orderausführungsmanagement — der Gegenstand von <ahref="bruecke.html#teil-anwendungen">Teil IV</a> dieses Buches.</li>
</ul>
<hr/>
<h2id="sec:einfuehrung-die-vier-universellen-bausteine-jedes-or">1.6 Die vier universellen Bausteine jedes OR-Problems</h2>
<p>Dies ist der wichtigste Abschnitt des Kapitels. Jedes Optimierungsproblem der Welt lässt sich — unabhängig von der Branche — auf vier Elemente reduzieren. Wenn Sie später vor einem neuen Problem sitzen, ist dies Ihre Checkliste.</p>
<figure>
<imgsrc="bilder_04/kap01_vier_bausteine.svg"alt="Abb. 1.2: Die vier universellen Bausteine eines OR-Modells"/>
<figcaptionaria-hidden="true">Abb. 1.2: Die vier universellen Bausteine eines OR-Modells</figcaption>
<p><strong>Die Leitfrage:</strong><em>Worüber darf ich überhaupt entscheiden?</em> Alles, was feststeht, ist kein Variable, sondern Parameter. Die häufigste Ursache für unbrauchbare Modelle ist eine falsch gewählte Variablendefinition — nicht ein falscher Solver.</p>
</blockquote>
<h3id="baustein-2-parameter-eingabedaten-c-a-b">Baustein 2 — Parameter / Eingabedaten (<spanclass="math inline">c, A, b</span>)</h3>
<p><strong>Die Leitfrage:</strong><em>Was ist gegeben und wird nicht entschieden?</em> Parameter sind der Ort, an dem Datenqualität über Modellqualität entscheidet. <ahref="finanzdaten.html#kap-finanzdaten">Kapitel 18</a> zeigt am Beispiel der Kovarianzmatrix, wie ein schlecht geschätzter Parameter ein mathematisch perfektes Modell wertlos macht.</p>
<li><spanclass="math inline">f(x)</span> — die Bewertungsfunktion: „Wie gut ist die Lösung <spanclass="math inline">x</span>?“</li>
<li><spanclass="math inline">\min_x</span> — „wähle dasjenige <spanclass="math inline">x</span>, das <spanclass="math inline">f</span> so klein wie möglich macht“</li>
<li>Das <spanclass="math inline">x</span> unter dem <spanclass="math inline">\min</span> sagt, <strong>worüber</strong> minimiert wird (über die Variablen, nicht etwa über die Parameter).</li>
</ul>
<p><strong>Ohne Formel gesagt:</strong> Von allen erlaubten Handlungsmöglichkeiten suchen wir diejenige mit den geringsten Kosten (bzw. dem höchsten Gewinn).</p>
</blockquote>
<blockquote>
<p><strong>Die Leitfrage:</strong><em>Was genau soll besser werden — und in welcher Einheit?</em> Wenn Sie die Einheit nicht nennen können (Euro, Minuten, Strafpunkte), ist die Zielfunktion noch nicht fertig. Mehrere gleichzeitige Ziele müssen entweder gewichtet in <strong>eine</strong> Zahl überführt oder hierarchisch nacheinander optimiert werden.</p>
<p><strong>Harte Nebenbedingungen (<em>hard constraints</em>)</strong> sind zwingend: gesetzliche Ruhezeit <spanclass="math inline">\ge 11</span> Stunden, Summe der Portfoliogewichte <spanclass="math inline">= 100\,\%</span>, maximales Verlustrisiko <spanclass="math inline">\le 5\,\%</span>. Wird eine einzige harte Bedingung verletzt, ist die Lösung mathematisch <strong>unzulässig</strong> (<em>infeasible</em>) — sie existiert für den Solver schlicht nicht.</p>
<p><strong>Weiche Nebenbedingungen (<em>soft constraints</em>)</strong> sind Wünsche: Mitarbeiterpräferenzen, möglichst wenig Umschichtung, gleichmäßige Lastverteilung. Sie werden über <strong>Strafkosten</strong> (<em>penalties</em>) in die Zielfunktion integriert und dürfen im Notfall verletzt werden — es kostet dann eben.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Harte Bedingung = „darf nicht“. Weiche Bedingung = „soll möglichst nicht, sonst kostet es X“. Die Entscheidung zwischen beiden ist eine der folgenreichsten im ganzen Modell: Zu viele harte Bedingungen erzeugen unlösbare Modelle (<ahref="praxisfallen.html#kap-praxisfallen">Kapitel 22</a>), zu wenige erzeugen Lösungen, die niemand akzeptiert.</p>
</blockquote>
<h3id="die-vier-bausteine-als-eine-formel">Die vier Bausteine als eine Formel</h3>
<p>Setzt man die vier Bausteine zusammen, entsteht die Standardform, in der Ihnen jedes Optimierungsmodell dieses Buches begegnen wird. Sie sieht auf den ersten Blick sperrig aus — deshalb steht daneben, was jede Zeile in Alltagssprache bedeutet.</p>
<p><spanclass="math display">
\max_{\mathbf{x}}\ \sum_{j=1}^{n} c_j x_j
\qquad \text{u. d. N.} \qquad
\sum_{j=1}^{n} a_{ij} x_j \le b_i \quad \forall i = 1,\dots,m,
\qquad x_j \ge 0
</span></p>
<divclass="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer</strong></p>
<table>
<colgroup>
<colstyle="width: 50%"/>
<colstyle="width: 50%"/>
</colgroup>
<thead>
<trclass="header">
<th>Mathematik</th>
<th>Alltagssprache</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><spanclass="math inline">x_j</span></td>
<td>Wie viel bauen / kaufen / einplanen wir von Produkt <spanclass="math inline">j</span>? Das ist die Stellschraube — der Solver entscheidet darüber.</td>
</tr>
<trclass="even">
<td><spanclass="math inline">c_j</span></td>
<td>Was bringt eine Einheit von <spanclass="math inline">j</span>? Im Schreinerei-Beispiel: 240 € beim Tisch, 60 € beim Stuhl.</td>
<td>„Suche unter allen erlaubten Mengenkombinationen die mit dem höchsten Gesamtertrag.“ Das <spanclass="math inline">\mathbf{x}</span> darunter sagt nur, worüber entschieden wird.</td>
</tr>
<trclass="odd">
<td><spanclass="math inline">a_{ij}</span></td>
<td>Wie viel von Ressource <spanclass="math inline">i</span> verbraucht eine Einheit von Produkt <spanclass="math inline">j</span>? Ein Tisch verbraucht 3 Montagestunden.</td>
</tr>
<trclass="even">
<td><spanclass="math inline">b_i</span></td>
<td>Wie viel von Ressource <spanclass="math inline">i</span> ist überhaupt da? 150 Montagestunden.</td>
<td>„Negative Mengen gibt es nicht.“ Trivial für Menschen, muss dem Solver aber gesagt werden.</td>
</tr>
</tbody>
</table>
<p><strong>Der ganze Ausdruck in einem Satz:</strong><em>Verteile die knappen Ressourcen so auf die Produkte, dass der Gesamtertrag maximal wird und kein Vorrat überzogen wird.</em></p>
</blockquote>
</div>
<p>Das <spanclass="math inline">\forall</span> („für alle“) ist dabei das Zeichen mit der größten praktischen Wirkung: Es verwandelt eine Zeile Mathematik in eine <code>for</code>-Schleife und damit in beliebig viele Nebenbedingungen. Im Schreinerei-Programm oben steckt es in den zwei <code>s.Add(...)</code>-Zeilen — bei 40 Ressourcen wären es 40, geschrieben in derselben einen Schleife.</p>
<h3id="die-bausteine-in-der-praxis-eine-vorlage">Die Bausteine in der Praxis: eine Vorlage</h3>
<p>Damit die Zerlegung zur Gewohnheit wird, hier eine Vorlage, die Sie bei jedem neuen Problem ausfüllen können — vor der ersten Codezeile.</p>
<tablestyle="width:100%;">
<colgroup>
<colstyle="width: 23%"/>
<colstyle="width: 64%"/>
<colstyle="width: 11%"/>
</colgroup>
<thead>
<trclass="header">
<th>Baustein</th>
<th>Leitfrage</th>
<th>Ihr Problem</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>Entscheidungsvariablen</td>
<td>Worüber darf ich entscheiden? Welcher Typ?</td>
<td></td>
</tr>
<trclass="even">
<td>Parameter</td>
<td>Was ist gegeben? Woher kommen die Daten?</td>
<td></td>
</tr>
<trclass="odd">
<td>Zielfunktion</td>
<td>Was soll besser werden? In welcher Einheit? Min oder Max?</td>
<td></td>
</tr>
<trclass="even">
<td>Harte Bedingungen</td>
<td>Was ist <em>unter keinen Umständen</em> erlaubt?</td>
<td></td>
</tr>
<trclass="odd">
<td>Weiche Bedingungen</td>
<td>Was ist unerwünscht, aber im Notfall hinnehmbar? Wie teuer?</td>
<td></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>✏️ Handrechnung 1.2: Zerlegen Sie dieses Problem</strong></p>
<p><em>Eine Bäckerei backt Brot und Brötchen. Ein Brot bringt 2,50 € Deckungsbeitrag und braucht 0,5 kg Mehl und 4 Minuten Ofenzeit; 10 Brötchen bringen 3,00 € und brauchen 0,6 kg Mehl und 3 Minuten Ofenzeit. Verfügbar sind 90 kg Mehl und 600 Minuten Ofenzeit. Aus Vertragsgründen müssen mindestens 40 Brote gebacken werden.</em></p>
<p>Füllen Sie die Vorlage aus, bevor Sie weiterlesen. — Auflösung:</p>
<p>Dieses Problem lösen Sie in Übung 1.5 selbst.</p>
</blockquote>
<p>Die Vorlage lässt sich fast wörtlich in Code übersetzen — wer die fünf Zeilen ausgefüllt hat, hat den größten Teil der Modellierungsarbeit schon erledigt:</p>
<spanid="cb5-5"><ahref="#cb5-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel Einfuehrung: Die Bausteine-Vorlage als ausfuellbares Python-Geruest.</span></span>
<spanid="cb5-7"><ahref="#cb5-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">Uebersetzt die Vier-Bausteine-Vorlage aus dem Abschnitt 'Die vier</span></span>
<spanid="cb5-8"><ahref="#cb5-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">universellen Bausteine jedes OR-Problems' direkt in Code: Wer sie</span></span>
<spanid="cb5-9"><ahref="#cb5-9"aria-hidden="true"tabindex="-1"></a><spanclass="co">auf Papier ausgefuellt hat, kann sie fast unveraendert in ein loesbares</span></span>
<spanid="cb5-10"><ahref="#cb5-10"aria-hidden="true"tabindex="-1"></a><spanclass="co">Modell umsetzen. Angewendet auf das Baeckerei-Beispiel der Handrechnung.</span></span>
<spanid="cb5-19"><ahref="#cb5-19"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Die vier Bausteine jedes OR-Problems - wortwoertlich aus der Vorlage</span></span>
<spanid="cb5-20"><ahref="#cb5-20"aria-hidden="true"tabindex="-1"></a><spanclass="co"> im Abschnitt 'Die vier universellen Bausteine' uebernommen."""</span></span>
<spanid="cb5-42"><ahref="#cb5-42"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Baeckerei-Beispiel aus der Handrechnung ---------------------------------</span></span>
<spanid="cb5-56"><ahref="#cb5-56"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Baut aus der ausgefuellten Vorlage mechanisch ein CP-SAT-Modell."""</span></span>
<spanid="cb5-57"><ahref="#cb5-57"aria-hidden="true"tabindex="-1"></a> modell <spanclass="op">=</span> cp_model.CpModel()</span>
<spanid="cb5-67"><ahref="#cb5-67"aria-hidden="true"tabindex="-1"></a> status <spanclass="op">=</span> loeser.Solve(modell)</span>
<spanid="cb5-68"><ahref="#cb5-68"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> status <spanclass="kw">not</span><spanclass="kw">in</span> (cp_model.OPTIMAL, cp_model.FEASIBLE):</span>
<spanid="cb5-76"><ahref="#cb5-76"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st">--- Aus der Vorlage mechanisch abgeleitetes Modell ---"</span>)</span>
<spanid="cb5-82"><ahref="#cb5-82"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Genau diese drei Codezeilen (Variablen, Add, Maximize) entstehen"</span>)</span>
<spanid="cb5-83"><ahref="#cb5-83"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"direkt aus den fuenf Zeilen der Vorlage - das ist der ganze Trick."</span>)</span></code></pre></div>
--- Aus der Vorlage mechanisch abgeleitetes Modell ---
Optimale Brote: 42
Optimale Broetchen-Zehner: 115
Maximaler Deckungsbeitrag: 450.00 EUR
Genau diese drei Codezeilen (Variablen, Add, Maximize) entstehen
direkt aus den fuenf Zeilen der Vorlage - das ist der ganze Trick.</code></pre>
<p>Mehl und Deckungsbeitrag stehen im selben Verhältnis für Brote wie für Brötchen (je 5 € pro kg Mehl) — deshalb ist die Aufteilung zwischen 42/115 nicht eindeutig, solange die 90 kg Mehl restlos verplant sind und mindestens 40 Brote gebacken werden; <strong>der maximale Deckungsbeitrag von 450 € dagegen ist es.</strong></p>
<hr/>
<h2id="sec:einfuehrung-erstes-python-vollbeispiel-ressourcenallokation-im-rechenzentrum">1.7 Erstes Python-Vollbeispiel: Ressourcenallokation im Rechenzentrum</h2>
<h3id="szenario">Szenario</h3>
<p>Ein quantitatives Handelshaus betreibt Serverkapazitäten für zwei Algorithmentypen:</p>
<ul>
<li><strong>Algorithmus A (Arbitrage-Bot):</strong> erzeugt <spanclass="math inline">150\,\text{€}</span> Gewinn pro Tag, benötigt <spanclass="math inline">2</span> Rechenkerne (vCPUs) und <spanclass="math inline">4\,\text{GB}</span> Arbeitsspeicher.</li>
<li><strong>Algorithmus B (Trendfolge-Bot):</strong> erzeugt <spanclass="math inline">250\,\text{€}</span> Gewinn pro Tag, benötigt <spanclass="math inline">5</span> Rechenkerne und <spanclass="math inline">6\,\text{GB}</span> Arbeitsspeicher.</li>
</ul>
<p><strong>Systemressourcen:</strong> höchstens <spanclass="math inline">40</span> vCPUs, höchstens <spanclass="math inline">60\,\text{GB}</span> RAM, und aus Marktliquiditätsgründen höchstens <spanclass="math inline">8</span> Arbitrage-Bots.</p>
<p><strong>Frage:</strong> Wie viele Instanzen von A und B maximieren den Tagesgewinn?</p>
<li><spanclass="math inline">x_A</span>, <spanclass="math inline">x_B</span> — Anzahl gestarteter Bots je Typ. Ganzzahlig, weil ein halber Bot nicht existiert.</li>
<li><spanclass="math inline">Z = 150x_A + 250x_B</span> — der Tagesgewinn in Euro: pro Arbitrage-Bot 150 €, pro Trendfolge-Bot 250 €.</li>
<p><strong>Ohne Formel gesagt:</strong> Starte so viele Bots wie möglich, aber verbrauche nicht mehr Rechenkerne und Arbeitsspeicher, als da sind — und nicht mehr als acht Arbitrage-Bots.</p>
</blockquote>
<blockquote>
<p><strong>✏️ Handrechnung 1.3: Das Optimum zu Fuß finden</strong></p>
<p>Bei nur zwei Variablen können wir die Kandidaten abgehen. Weil B mehr Gewinn bringt (250 € gegen 150 €), liegt die Vermutung nahe, möglichst viele B-Bots zu starten.</p>
<p>Beachten Sie das Überraschende: Die naive Strategie „nur den lukrativeren Bot“ (<spanclass="math inline">0, 8</span>) liefert <strong>2000 €</strong> — ganze 300 € weniger als die Mischung. Genau solche Effekte machen Optimierung nötig: Der beste Gesamtplan besteht selten aus lauter lokal besten Einzelentscheidungen.</p>
</blockquote>
<h3id="umsetzung-mit-google-or-tools">Umsetzung mit Google OR-Tools</h3>
<spanid="cb7-5"><ahref="#cb7-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel Einfuehrung: Erstes Optimierungsmodell mit Google OR-Tools (CP-SAT).</span></span>
<spanid="cb7-6"><ahref="#cb7-6"aria-hidden="true"tabindex="-1"></a><spanclass="co">Problem: Server-Allokation für Trading-Bots.</span></span>
<spanid="cb7-14"><ahref="#cb7-14"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Parameter (die "Fakten" des Problems) ---------------------------------</span></span>
<spanid="cb7-15"><ahref="#cb7-15"aria-hidden="true"tabindex="-1"></a>GEWINN_A, GEWINN_B <spanclass="op">=</span><spanclass="dv">150</span>, <spanclass="dv">250</span><spanclass="co"># Euro pro Bot und Tag</span></span>
<spanid="cb7-16"><ahref="#cb7-16"aria-hidden="true"tabindex="-1"></a>CPU_A, CPU_B <spanclass="op">=</span><spanclass="dv">2</span>, <spanclass="dv">5</span><spanclass="co"># vCPU-Bedarf je Bot</span></span>
<spanid="cb7-17"><ahref="#cb7-17"aria-hidden="true"tabindex="-1"></a>RAM_A, RAM_B <spanclass="op">=</span><spanclass="dv">4</span>, <spanclass="dv">6</span><spanclass="co"># GB Arbeitsspeicher je Bot</span></span>
<spanid="cb7-23"><ahref="#cb7-23"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Baut das Modell, löst es und gibt eine Auswertung aus."""</span></span>
<spanid="cb7-30"><ahref="#cb7-30"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Die obere Grenze ist bewusst großzügig; die echte Beschränkung</span></span>
<spanid="cb7-31"><ahref="#cb7-31"aria-hidden="true"tabindex="-1"></a><spanclass="co"># formulieren wir unten als Nebenbedingung, damit das Modell die</span></span>
<spanid="cb7-51"><ahref="#cb7-51"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> status <spanclass="kw">not</span><spanclass="kw">in</span> (cp_model.OPTIMAL, cp_model.FEASIBLE):</span>
<spanid="cb7-53"><ahref="#cb7-53"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> status <spanclass="op">==</span> cp_model.INFEASIBLE:</span>
<spanid="cb7-54"><ahref="#cb7-54"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"Das Modell ist unlösbar - die Bedingungen widersprechen sich."</span>)</span>
<spanid="cb7-65"><ahref="#cb7-65"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">' (beweisbar optimal)'</span><spanclass="cf">if</span> status <spanclass="op">==</span> cp_model<spanclass="sc">.</span>OPTIMAL <spanclass="cf">else</span><spanclass="st">' (zulässig, nicht bewiesen)'</span><spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb7-79"><ahref="#cb7-79"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 8. Selbstkontrolle: Sind wirklich alle Bedingungen eingehalten?</span></span>
<spanid="cb7-84"><ahref="#cb7-84"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 9. Zum Vergleich: die naive Strategie "nur der lukrativere Bot"</span></span>
<spanid="cb7-89"><ahref="#cb7-89"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"Vorteil der Optimierung: </span><spanclass="sc">{</span>gewinn <spanclass="op">-</span> naiv_gewinn<spanclass="sc">:,.2f}</span><spanclass="ss"> EUR pro Tag "</span></span>
<p>Das Ergebnis deckt sich mit der Handrechnung <em>Das Optimum zu Fuß finden</em> — ein gutes Zeichen. <strong>Prüfen Sie Solver-Ergebnisse wo immer möglich gegen eine unabhängige Rechnung</strong>, gerade am Anfang.</p>
<blockquote>
<p><strong>💻 Code-Durchgang</strong></p>
<table>
<colgroup>
<colstyle="width: 11%"/>
<colstyle="width: 12%"/>
<colstyle="width: 75%"/>
</colgroup>
<thead>
<trclass="header">
<th>Stelle</th>
<th>Was passiert</th>
<th>Didaktischer Hinweis</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>Konstanten oben</td>
<td>alle Parameter an einer Stelle</td>
<td>Nie Zahlen im Modellcode verstreuen — sonst findet man sie beim Ändern nicht wieder</td>
</tr>
<trclass="even">
<td><code>NewIntVar(0, 100, ...)</code></td>
<td>ganzzahlige Variable mit weiten Grenzen</td>
<td>Grenzen müssen endlich sein; 100 ist bewusst unkritisch groß</td>
<td>Marktlimit als <strong>Nebenbedingung</strong></td>
<td>Alternativ liesse sich dieses Limit auch in die Variablengrenze packen. Beides funktioniert — aber als Nebenbedingung bleibt das Modell zur mathematischen Formulierung deckungsgleich und man kann später den Schattenpreis abfragen</td>
</tr>
<trclass="even">
<td><code>loeser.StatusName(status)</code></td>
<td>echter Status statt fester Text</td>
<td>Ein fest codierter Text wie „OPTIMAL“ würde unabhängig vom tatsächlichen Ergebnis ausgegeben — deshalb immer den echten Solver-Status auswerten</td>
</tr>
<trclass="odd">
<td><code>assert ...</code></td>
<td>Selbstkontrolle nach dem Lösen</td>
<td>Kostet nichts und fängt Modellierungsfehler, die der Solver nicht bemerken kann</td>
</tr>
<trclass="even">
<td>Vergleich mit naiver Strategie</td>
<td>quantifiziert den Nutzen</td>
<td>Die Frage „Was bringt die Optimierung überhaupt?“ kommt in jedem Projekt — beantworten Sie sie ungefragt</td>
</tr>
</tbody>
</table>
</blockquote>
<blockquote>
<p><strong>⚠️ Typische Fehler</strong></p>
<ul>
<li><strong>Variablengrenzen zu eng gewählt.</strong><code>NewIntVar(0, 5, ...)</code> würde hier stillschweigend die Lösung <spanclass="math inline">x_B = 5</span> erzwingen und ein falsches „Optimum“ liefern. Grenzen sind Nebenbedingungen — behandeln Sie sie als solche.</li>
<li><strong>Status nicht geprüft.</strong> Ein Programm, das bei <code>INFEASIBLE</code> weiterläuft und <code>solver.Value(...)</code> abfragt, liefert entweder Unsinn oder stürzt ab.</li>
<li><strong>Ganzzahligkeit vergessen.</strong> Mit kontinuierlichen Variablen liefert dasselbe Modell <spanclass="math inline">x_A = 7{,}5</span>, <spanclass="math inline">x_B = 5{,}0</span> und <spanclass="math inline">Z = 2375</span> € — ein Wert, den es real nicht gibt. Warum man solche Werte <strong>nicht</strong> einfach runden darf, ist Thema von <ahref="milp.html#kap-milp">Kapitel 6</a>.</li>
</ul>
</blockquote>
<blockquote>
<p><strong>📎 Warum dieses Programm alles selbst macht</strong></p>
<p>Wenige Seiten weiter, in der Excel-Brücke, steht ein Programm, das mit 96 Zeilen auskommt, weil es die immer gleichen Arbeitsschritte aus dem gemeinsamen Modul <code>or_kern.py</code> bezieht (<ahref="praxisfallen.html#sec:praxisfallen-or-kern">Abschnitt 22.6</a>). Warum tut <code>Bot_Allokation.py</code> das nicht auch?</p>
<p>Weil es hier nicht darum geht, <strong>wenig Code</strong> zu schreiben, sondern darum zu sehen, wie ein Modell <strong>entsteht</strong>: Variablen, Nebenbedingungen, Zielfunktion, Status, Prüfung — jeder Schritt einzeln und sichtbar. Eine Abstraktion, die diese Schritte zusammenfasst, ist genau dann ein Gewinn, wenn man sie schon einmal von Hand gegangen ist; vorher versteckt sie nur, was man verstehen will.</p>
<p>Das ist eine allgemeine Regel für eigene Projekte: <strong>Erst die Wiederholung rechtfertigt die Abstraktion.</strong> Wer sein erstes Modell gleich als Framework baut, hat ein Framework für einen Fall — und die Erfahrung, welche Fälle es tragen müsste, hat er noch nicht gemacht.</p>
</blockquote>
<hr/>
<h2id="sec:einfuehrung-excel-bruecke">1.8 Von Excel zu Python: Ihre Daten liegen schon da</h2>
<p>In den allermeisten Betrieben liegen die Zahlen, die ein Optimierungsmodell braucht, nicht in einer Datenbank, sondern in einer Tabellenkalkulation. Und viele Leserinnen und Leser kennen Optimierung bisher genau von dort: über <em>Daten → Solver</em> in Excel. Dieser Abschnitt schlägt die Brücke — in beide Richtungen.</p>
<divclass="card card-excel">
<blockquote>
<p><strong>📊 Excel-Brücke: dieselben Begriffe, andere Werkzeuge</strong></p>
<table>
<colgroup>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
<colstyle="width: 33%"/>
</colgroup>
<thead>
<trclass="header">
<th>Im Excel-Solver</th>
<th>In Python</th>
<th>Anmerkung</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>„Veränderbare Zellen“</td>
<td><code>x = solver.NumVar(...)</code></td>
<td>Der Zellbereich wird zu benannten Variablen.</td>
</tr>
<trclass="even">
<td>„Zielzelle“ + Max/Min</td>
<td><code>solver.Maximize(...)</code></td>
<td>Statt einer Formel in einer Zelle ein Ausdruck im Code.</td>
</tr>
<trclass="odd">
<td>„Nebenbedingungen“-Liste</td>
<td><code>solver.Add(...)</code></td>
<td>Eine Zeile je Regel — oder eine Schleife für 10 000 Regeln.</td>
<td>Gleiche Bedeutung, gleiche Konsequenz für die Laufzeit.</td>
</tr>
<trclass="odd">
<td>Simplex-LP / GRG / Evolutionär</td>
<td>GLOP / SLSQP / CP-SAT</td>
<td>In Excel drei Auswahlpunkte, in Python drei Bibliotheken (<ahref="oekosystem.html#kap-oekosystem">Kapitel 3</a>).</td>
</tr>
<trclass="even">
<td>„Sensitivitätsbericht“</td>
<td><code>bedingung.dual_value()</code></td>
<td>Der Schattenpreis — <ahref="lp.html#kap-lp">Kapitel 5</a> erklärt, was er bedeutet.</td>
</tr>
<trclass="odd">
<td>200 Zeilen sind das Limit</td>
<td>keine praktische Grenze</td>
<td>Der Excel-Solver ist auf 200 veränderbare Zellen begrenzt.</td>
</tr>
</tbody>
</table>
</blockquote>
</div>
<p><strong>Was Sie damit gewinnen</strong> ist weniger die Rechenleistung als die Nachvollziehbarkeit: Ein Python-Modell lässt sich versionieren, testen, in einer Schleife über hundert Szenarien laufen lassen und einem Kollegen erklären. Eine gewachsene Excel-Mappe mit Formeln quer über vier Blätter kann das alles nicht — und niemand traut sich mehr, sie anzufassen.</p>
<p>Das folgende Programm zeigt den ganzen Weg an einem Stück. Es legt die Eingabemappe beim ersten Start selbst an, damit Sie es sofort ausführen können; danach ersetzen Sie die Datei durch Ihre eigene. Solange die Spalten gleich heißen, ändert sich am Code nichts.</p>
<blockquote>
<p><strong>📎 Zum Modul <code>or_kern.py</code></strong></p>
<p>Die vier Arbeitsschritte — lesen, lösen, prüfen, schreiben — stehen nicht in diesem Programm, sondern in <code>or_kern.py</code>. Das ist ein gemeinsamer Unterbau, den alle Programme dieses Buchs benutzen können; er liegt im selben Verzeichnis und ist in <ahref="praxisfallen.html#sec:praxisfallen-or-kern">Abschnitt 22.6</a> vollständig abgedruckt und erklärt.</p>
<p>Sie müssen ihn jetzt noch nicht verstehen. Wichtig ist nur die Aufteilung: <strong>Hier</strong> steht, was diesen Fall ausmacht — dort steht, was jedes Optimierungsprogramm braucht. Genau deshalb ist der Code unten so kurz.</p>
<spanid="cb9-7"><ahref="#cb9-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">Das Programm bildet den vollstaendigen Arbeitsweg ab, den ein Excel-Modell im</span></span>
<spanid="cb9-8"><ahref="#cb9-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">Betrieb sonst von Hand geht:</span></span>
<spanid="cb9-12"><ahref="#cb9-12"aria-hidden="true"tabindex="-1"></a><spanclass="co">Damit es ohne Vorbereitung laeuft, legt es die Eingabedatei beim ersten Start</span></span>
<spanid="cb9-13"><ahref="#cb9-13"aria-hidden="true"tabindex="-1"></a><spanclass="co">selbst an. Ersetzen Sie sie durch Ihre eigene Datei - solange die Spalten</span></span>
<spanid="cb9-14"><ahref="#cb9-14"aria-hidden="true"tabindex="-1"></a><spanclass="co">gleich heissen, aendert sich am Code nichts.</span></span>
<spanid="cb9-16"><ahref="#cb9-16"aria-hidden="true"tabindex="-1"></a><spanclass="co">Die vier Arbeitsschritte kommen aus or_kern.py (Kapitel Praxisfallen). Was</span></span>
<spanid="cb9-17"><ahref="#cb9-17"aria-hidden="true"tabindex="-1"></a><spanclass="co">hier steht, ist nur noch das, was diesen Fall ausmacht - der Rest ist</span></span>
<spanid="cb9-18"><ahref="#cb9-18"aria-hidden="true"tabindex="-1"></a><spanclass="co">gemeinsamer Unterbau. Wer das Modul nicht kennt: Es ist im Buch vollstaendig</span></span>
<spanid="cb9-19"><ahref="#cb9-19"aria-hidden="true"tabindex="-1"></a><spanclass="co">abgedruckt und liegt im selben Verzeichnis.</span></span>
<spanid="cb9-42"><ahref="#cb9-42"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Schreibt die Eingabemappe, wie sie auch aus dem Controlling kaeme."""</span></span>
<spanid="cb9-63"><ahref="#cb9-63"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 1. Lesen und pruefen. Faellt hier etwas auf, ist der Fehler noch</span></span>
<spanid="cb9-64"><ahref="#cb9-64"aria-hidden="true"tabindex="-1"></a><spanclass="co"># zuzuordnen - spaeter waere er nur noch eine merkwuerdige Zahl.</span></span>
<spanid="cb9-65"><ahref="#cb9-65"aria-hidden="true"tabindex="-1"></a> problem <spanclass="op">=</span> lade_produktionsproblem(EINGABE)</span>
<spanid="cb9-67"><ahref="#cb9-67"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 2. Loesen. Einzige solverabhaengige Zeile des ganzen Programms -</span></span>
<spanid="cb9-68"><ahref="#cb9-68"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 'loese_mit_scipy' waere ein Einzeiler-Wechsel.</span></span>
<spanid="cb9-71"><ahref="#cb9-71"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 3. Abnahmepruefung gegen die Anforderungen, ohne den Solver zu fragen.</span></span>
<spanid="cb9-77"><ahref="#cb9-77"aria-hidden="true"tabindex="-1"></a><spanclass="co"># 4. Zurueckschreiben - im Format, das die Fachabteilung ohnehin benutzt.</span></span>
<spanid="cb9-81"><ahref="#cb9-81"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" PRODUKTIONSPLAN AUS DER EXCEL-MAPPE"</span>)</span>
<spanid="cb9-91"><ahref="#cb9-91"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Die Schattenpreise beantworten die Frage, die der Plan nicht stellt:</span></span>
<spanid="cb9-92"><ahref="#cb9-92"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Was waere eine zusaetzliche Einheit dieser Ressource wert?</span></span>
<spanid="cb9-94"><ahref="#cb9-94"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" eine Einheit </span><spanclass="sc">{</span>ressource<spanclass="sc">:<18}</span><spanclass="ss"> mehr waere wert: </span><spanclass="sc">{</span>preis<spanclass="sc">:7.2f}</span><spanclass="ss"> EUR"</span>)</span>
<td>liest die Mappe <strong>und prüft sie</strong></td>
<td>Leere Zellen, fehlende Spalten, Kapazitäten von null oder Ressourcen ohne Kapazitätsangabe scheitern hier — nicht erst beim Lösen, wo der Fehler nur noch eine merkwürdige Zahl wäre.</td>
</tr>
<trclass="even">
<td><code>loese_mit_glop(problem)</code></td>
<td>die einzige solverabhängige Zeile</td>
<td><code>loese_mit_scipy</code> wäre ein Einzeiler-Wechsel. Alles davor und danach bleibt unverändert.</td>
<td>Sie bekommt nur die ausgegebene Lösung und die Anforderungen. Eine Prüfung aus denselben Bausteinen wie das Modell prüft das Modell gegen sich selbst.</td>
</tr>
<trclass="even">
<td><code>raise SystemExit(...)</code></td>
<td>bricht bei Beanstandung ab</td>
<td>Ein Programm, das trotz durchgefallener Prüfung eine Datei schreibt, ist gefährlicher als eines, das abstürzt.</td>
</tr>
<trclass="odd">
<td><code>loesung.schattenpreise</code></td>
<td>Dualwerte aus dem Lösungsobjekt</td>
<td>Sie sind ein <strong>solverunabhängiger</strong> Begriff und stehen deshalb im Ergebnis, nicht im Solvercode.</td>
</tr>
</tbody>
</table>
</blockquote>
<p><strong>Das Ergebnis liest sich von selbst:</strong> Das Regal — obwohl mit 130 € Deckungsbeitrag scheinbar attraktiv — wird gar nicht gebaut. Die Schattenpreise sagen warum: Ein Regal verbraucht 2 Montagestunden zu je 40 € und 4 m² Material zu je 20 €, bindet also Ressourcen im Wert von <spanclass="math inline">2 \cdot 40 + 4 \cdot 20 = 160</span> € und bringt nur 130 € ein. Es verdient seinen Platz in der Werkstatt nicht. Diese Art von Aussage — begründet, in Euro, für den Chef nachvollziehbar — ist der eigentliche Ertrag der Optimierung.</p>
<blockquote>
<p><strong>⚠️ Typische Fehler beim Umstieg von Excel</strong></p>
<ul>
<li><strong>Zellbezüge statt Namen.</strong> Wer im Code mit <code>df.iloc[:, 3]</code> arbeitet, baut denselben Fehler nach, der in Excel-Mappen <code>=SUMME(D2:D40)</code> so gefährlich macht: Eine eingefügte Spalte verschiebt alles. Sprechen Sie Spalten <strong>über ihren Namen</strong> an.</li>
<li><strong>Einheiten mischen.</strong> Wenn eine Spalte Stunden und eine Minuten enthält, merkt das weder Excel noch der Solver — nur der Disponent, wenn der Plan nicht aufgeht.</li>
<li><strong>Das Ergebnis nur auf den Bildschirm schreiben.</strong> Was nicht in einer Datei landet, die die Fachabteilung öffnen kann, wird nicht benutzt.</li>
<p>Lösungen zu allen Aufgaben: <ahref="anhang-loesungen.html#sec:loesungen-einfuehrung">Abschnitt A.1</a>.</p>
</blockquote>
<p><strong>Aufgabe 1.1 ⭐ — Abgrenzung.</strong> Ordnen Sie jede Frage der richtigen Analytik-Stufe (deskriptiv / prädiktiv / präskriptiv) zu und begründen Sie in einem Satz: (a) „Wie viele Pakete haben wir letzten Monat zugestellt?“ (b) „Wie viele Pakete werden wir nächsten Dezember zustellen?“ (c) „Welche Tour soll Fahrzeug 3 morgen fahren?“ (d) „Welche Kunden werden voraussichtlich kündigen?“ (e) „Welchen dieser Kunden sollen wir mit unserem begrenzten Rabattbudget halten?“</p>
<p><strong>Aufgabe 1.2 ⭐ — Hart oder weich?</strong> Entscheiden Sie für jede Bedingung eines Klinik-Dienstplans, ob sie hart oder weich modelliert werden sollte, und begründen Sie: (a) Gesetzliche Ruhezeit von 11 Stunden zwischen zwei Diensten. (b) Frau Meier möchte freitags nicht arbeiten. (c) In jeder Nachtschicht muss mindestens eine examinierte Fachkraft anwesend sein. (d) Die Dienste sollen gleichmäßig über das Team verteilt sein. (e) Niemand arbeitet mehr als 10 Tage am Stück.</p>
<p><strong>Aufgabe 1.3 ⭐⭐ — Kombinatorik selbst rechnen.</strong> Ein Speditionsdisponent muss 12 Aufträge auf einen einzigen Lkw in eine Reihenfolge bringen. (a) Wie viele Reihenfolgen gibt es? (b) Der Rechner prüft 5 Millionen Reihenfolgen pro Sekunde. Wie lange dauert das vollständige Durchprobieren? (c) Wie lange dauert es bei 13 Aufträgen? Um welchen Faktor ist das mehr? (d) Wie viele Aufträge könnte man in einer Stunde noch vollständig durchprobieren?</p>
<p><strong>Aufgabe 1.4 ⭐⭐ — Modell lesen.</strong> Gegeben sei das Modell <spanclass="math display">\max\ 3x_1 + 5x_2 \quad \text{u. d. N.} \quad x_1 \le 4,\ \ 2x_2 \le 12,\ \ 3x_1 + 2x_2 \le 18,\ \ x_1, x_2 \ge 0.</span> (a) Benennen Sie die vier Bausteine. (b) Ist <spanclass="math inline">(2, 6)</span> zulässig? Ist <spanclass="math inline">(4, 3)</span> zulässig? Berechnen Sie jeweils <spanclass="math inline">Z</span>. (c) Finden Sie durch Probieren die beste ganzzahlige Lösung.</p>
<p><strong>Aufgabe 1.5 ⭐⭐⭐ — Die Bäckerei programmieren.</strong> Setzen Sie das Bäckerei-Problem der Handrechnung <em>Zerlegen Sie dieses Problem</em> mit CP-SAT um. Geben Sie die optimale Produktionsmenge, den Deckungsbeitrag und die Auslastung von Mehl und Ofen aus. Prüfen Sie Ihr Ergebnis mit <code>assert</code>-Anweisungen.</p>
<p><strong>Aufgabe 1.6 ⭐⭐⭐ — Sensitivität durch Ausprobieren.</strong> Erweitern Sie <code>Bot_Allokation.py</code> so, dass es das Modell in einer Schleife für RAM-Kapazitäten von 54 bis 72 GB (in Schritten von 2) löst und eine Tabelle <code>RAM | x_A | x_B | Gewinn | Gewinnzuwachs pro zusätzlichem GB</code> ausgibt. (a) Ab welcher RAM-Menge steigt der Gewinn nicht mehr? Warum? (b) Was sagt Ihnen der „Gewinnzuwachs pro GB“ intuitiv? (Der Begriff dafür — <em>Schattenpreis</em> — folgt in <ahref="lp.html#kap-lp">Kapitel 5</a>.)</p>
<p><strong>Aufgabe 1.7 ⭐⭐⭐ — Eigenes Problem zerlegen.</strong> Wählen Sie ein Entscheidungsproblem aus Ihrem eigenen Alltag oder Beruf (Beispiele: Wochenplan für Sportkurse, Sitzordnung bei einer Feier, Aufteilung eines Budgets auf Projekte, Reihenfolge von Hausarbeiten). Füllen Sie die Vorlage aus <ahref="#sec:einfuehrung-die-vier-universellen-bausteine-jedes-or">Abschnitt 1.6</a> vollständig aus. Notieren Sie außerdem: Woher kämen die Daten? Wer müsste das Ergebnis akzeptieren? — Heben Sie diese Notiz auf; sie ist der Ausgangspunkt für Ihr eigenes Projekt in der <ahref="projektwerkstatt.html">Projektwerkstatt</a>.</p>
<hr/>
<h2id="sec:einfuehrung-denkfehler">1.10 Finde den Denkfehler</h2>
<p>Ein Programm, das abstürzt, ist harmlos — Sie sehen sofort, dass etwas nicht stimmt. Gefährlich sind Programme, die fehlerfrei durchlaufen, einen Status <code>OPTIMAL</code> melden und eine Zahl liefern, die betriebswirtschaftlich Unsinn ist. Genau solche Fälle üben wir hier.</p>
<p>Eine Kollegin hat das Schreinerei-Modell aus dem Kapitelanfang leicht umgebaut — sie fand die Schleife übersichtlicher als zwei einzelne <code>Add</code>-Aufrufe. Das Programm läuft, der Solver meldet <code>OPTIMAL</code>, und der Deckungsbeitrag ist plötzlich <strong>18 600 €</strong> statt 10 800 €. Der Chef ist begeistert. Sollte er das sein?</p>
<p><strong>Ihre Aufgabe:</strong> (a) Rechnen Sie nach, wie viele Montagestunden dieser Plan tatsächlich braucht. (b) Benennen Sie den Modellierungsfehler in einem Satz. (c) Korrigieren Sie ihn. (d) Formulieren Sie eine <code>assert</code>-Anweisung, die diesen Fehler künftig automatisch fängt.</p>
<p><strong>🎯 Merksatz</strong> Ein Optimierungsergebnis, das deutlich besser ausfällt als erwartet, ist zuerst einmal ein <strong>Fehlerverdacht</strong> — kein Grund zur Freude. Freuen Sie sich erst, nachdem Sie nachgerechnet haben, dass die Lösung alle Regeln einhält.</p>
</blockquote>
<hr/>
<h2id="sec:einfuehrung-quiz">1.11 Micro-Quiz</h2>
<divclass="card card-quiz">
<blockquote>
<p><strong>❓ Micro-Quiz 1: Drei Fragen zum Selbstcheck</strong></p>
<p>Genau eine Antwort ist jeweils richtig. Auflösung in <ahref="anhang-loesungen.html#quiz-loesung-einfuehrung">Anhang A</a>.</p>
<p><strong>1. Ein Vertriebsleiter fragt: „Welche fünf unserer 80 Kunden sollen wir mit dem Restbudget besuchen?“ Welcher Analytik-Stufe entspricht das?</strong> (a) Deskriptiv — es geht um vorhandene Kundendaten. (b) Prädiktiv — man muss den Umsatz je Kunde vorhersagen. (c) Präskriptiv — gesucht ist eine Handlungsanweisung unter einer Budgetgrenze.</p>
<p><strong>2. In einem Dienstplanmodell führt die Regel „Frau Meier möchte freitags frei haben“ als harte Nebenbedingung dazu, dass der Solver <code>INFEASIBLE</code> meldet. Was ist die fachlich richtige Reaktion?</strong> (a) Die Regel als weiche Bedingung mit Strafkosten formulieren. (b) Einen leistungsfähigeren Solver einsetzen. (c) Die Variablengrenzen erhöhen, damit mehr Lösungen möglich werden.</p>
<p><strong>3. Warum genügt es bei der Reihenfolgeplanung von 20 Aufträgen nicht, einen tausendmal schnelleren Rechner zu kaufen?</strong> (a) Weil Solver-Bibliotheken nicht parallelisiert sind. (b) Weil <spanclass="math inline">20!</span> so groß ist, dass Faktor 1000 die Rechenzeit von Jahrzehnten nur auf Wochen drückt — und schon 24 Aufträge wieder hoffnungslos sind. (c) Weil Fließkommazahlen bei großen Fakultäten ungenau werden.</p>
<li><strong>Operations Research ist präskriptiv:</strong> Es beantwortet nicht, was war oder was kommt, sondern was zu tun ist.</li>
<li><strong>Kombinatorische Explosion</strong><strong>ist eine Wand, keine Steigung.</strong> Zwischen „in Minuten machbar“ und „in Jahrmillionen unmöglich“ liegen oft nur wenige zusätzliche Objekte. Solver umgehen das nicht durch Geschwindigkeit, sondern durch Struktur.</li>
<li><strong>Jedes Modell besteht aus vier Bausteinen:</strong> Entscheidungsvariablen, Parameter, Zielfunktion, Nebenbedingungen. Diese Zerlegung ist die eigentliche Arbeit; die Solverwahl ergibt sich danach fast von selbst.</li>
<li><strong>Harte und weiche Bedingungen sind eine Entwurfsentscheidung</strong> mit großen Folgen für Lösbarkeit und Akzeptanz.</li>
<li><strong>Ihre Daten liegen schon in Excel.</strong><code>pandas.read_excel()</code> holt sie ab, der Solver rechnet, <code>to_excel()</code> gibt das Ergebnis im gewohnten Format zurück. Der Gewinn gegenüber einer Solver-Mappe ist nicht Rechenleistung, sondern Nachvollziehbarkeit: versionierbar, testbar, hundertmal in einer Schleife ausführbar.</li>
<li><strong>Prüfen Sie jedes Ergebnis</strong> — gegen eine Handrechnung, gegen <code>assert</code>-Anweisungen und gegen eine naive Vergleichsstrategie. Ein unerwartet gutes Ergebnis ist ein Fehlerverdacht, keine gute Nachricht.</li>
</ul>
<p><strong>Ausblick.</strong><ahref="fundament.html#kap-fundament">Kapitel 2</a> liefert die Sprache, um Modelle mit hunderten Variablen kompakt aufzuschreiben: Vektoren, Matrizen und die geometrische Sicht auf den zulässigen Bereich. Dort lernen wir auch die Eigenschaft kennen, die darüber entscheidet, ob ein Problem verlässlich lösbar ist: <strong>Konvexität</strong>.</p>