Glossar-Links: Fachbegriffe im Fließtext automatisch verlinken

Auto-Linking im Build: Glossar-Begriffe werden im Fließtext automatisch
erkannt und als Link auf den Glossar-Eintrag gesetzt — erste Nennung je
Begriff je Datei, dezent gestrichelte Unterstreichung im HTML, hyperref
im PDF. Kein manuelles Markieren bei Textänderungen nötig.

Neue Infrastruktur:
- glossar_eintraege_04.py: glossar_slug() + LINK_TEXTE-Map (automatisch
  generiert aus EINTRAEGEN + Abkürzungs-Extraktion)
- erzeuge_glossar_04.py: jeder Eintrag bekommt {#gloss:<slug>}-Anchor
- build_version_04.py: resolve_glossar() — Auto-Linking + {gl:}-Marke
- web_04/site.css: .glossar-link (dezent, nicht wie Navigationslink)

5 neue Glossar-Einträge (waren als Begriff im Text, aber nicht im Glossar):
Solver, Scheduling, Graphen, Backtest, DCP (Disciplined Convex Programming)

Schutzmechanismen:
- Code-Blöcke, Inline-Code, {idx:}/{ref:}-Marken, Markdown-Links und
  Überschriften werden nicht verlinkt
- Verschachtelung verhindert: 'CP-SAT-Solver' als Ganzes, nicht 'CP'+[
  -'SAT'+[-'Solver'
- 'OR' als 2-Zeichen-Abkürzung nicht auto-verlinkt (False Positive in
  'OR-Tools'); {gl:OR} als manuelle Marke
- Glossar selbst nicht auto-verlinkt (sonst Self-Links)
- reflow_markdown(): ::: als Block-Grenze (sonst kollabiert fenced div)

619 Glossar-Links im Gesamtdokument, 239 Anchor im Glossar, 34 im Vorwort.
This commit is contained in:
dschlueter 2026-09-10 17:40:29 +02:00
commit e8abb66d75
52 changed files with 4316 additions and 1921 deletions

View file

@ -105,7 +105,7 @@
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Um den Normalfall, den die bisherigen Kapitel umgangen haben: Es gibt nicht <em>ein</em> Ziel, sondern zwei, die gegeneinander stehen. Kosten und CO₂, Termintreue und Bestand, Rendite und Risiko.</p>
<p><strong>Voraussetzungen:</strong> <a href="modellierung.html#kap-modellierung">Kapitel 4</a> (die Zielgröße als Entscheidung) und <a href="milp.html#kap-milp">Kapitel 6</a>. Für den Denkfehler hilft <a href="markowitz.html#kap-markowitz">Kapitel 19</a>.</p>
<p><strong>Danach können Sie:</strong> begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse <strong>grundsätzlich</strong> nicht erreichen kann; eine vollständige Pareto-Front mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.</p>
<p><strong>Danach können Sie:</strong> begründen, warum eine gewichtete Summe bei ganzzahligen Modellen Kompromisse <strong>grundsätzlich</strong> nicht erreichen kann; eine vollständige <a href="anhang-glossar.md#gloss:pareto-front" class="glossar-link">Pareto-Front</a> mit dem ε-Constraint-Verfahren berechnen; und einer Geschäftsführung eine Entscheidung vorlegen, die sie tatsächlich treffen kann.</p>
<p><strong>Zeitbedarf:</strong> ca. 4 Stunden.</p>
<p><strong>Programme:</strong><br />
<code>Mehrziel_Pareto.py</code></p>
@ -120,7 +120,7 @@
<p>Eine Spedition vergibt fünf Sendungen an LKW, Bahn oder Kombinierten Verkehr. Der LKW ist billig und schmutzig, die Bahn teuer und sauber — und es sind nur zwei Trassen frei.</p>
<p>Statt zu fragen „wie wichtig ist uns CO₂?” fragen wir: <strong>Was bekommen wir für zwei Prozent mehr Geld?</strong></p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</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](anhang<span class="op">-</span>glossar.md<span class="co">#gloss:linprog){.glossar-link}</span></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"># LKW Bahn Kombi LKW billig+schmutzig, Bahn teuer+sauber</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>kosten <span class="op">=</span> np.array([[ <span class="dv">900</span>, <span class="dv">1150</span>, <span class="dv">1020</span>], [ <span class="dv">620</span>, <span class="dv">790</span>, <span class="dv">700</span>], [<span class="dv">1300</span>, <span class="dv">1660</span>, <span class="dv">1470</span>],</span>
@ -637,10 +637,10 @@ dem CO2-Preis, den das Unternehmen ohnehin ansetzt.
</div>
<p><strong>Warum es keine Frage des Rasters ist.</strong><span class="math inline">c(x) + w\,e(x)</span> minimieren” heißt geometrisch: eine Gerade der Steigung <span class="math inline">-1/w</span> von links unten an die Punktwolke schieben und schauen, welchen Punkt sie zuerst berührt. Eine Gerade berührt immer einen <strong>Eckpunkt der unteren konvexen Hülle</strong>. Punkte, die oberhalb der Hülle liegen, werden von keiner Geraden zuerst getroffen — für kein <span class="math inline">w</span>, bei beliebig feiner Abstufung.</p>
<p>Das Programm rechnet diese Hülle in <code>untere_huelle()</code> unabhängig aus und vergleicht: Genau die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe steht in der Ausgabe.</p>
<p><strong>Was Sie in der Abbildung sehen.</strong> Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht <span class="math inline">w</span>. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der Solver als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in <a href="#sec:mehrziel-epsilon">Abschnitt 14.7</a> geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.</p>
<p><strong>Was Sie in der Abbildung sehen.</strong> Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht <span class="math inline">w</span>. Die untere berührt die Hülle in zwei Ecken — eine von beiden meldet der <a href="anhang-glossar.md#gloss:solver" class="glossar-link">Solver</a> als Optimum. Die obere geht durch einen pareto-optimalen Punkt, der damit für dieses Gewicht chancenlos ist, und zwar nicht knapp: Er liegt eine ganze Stufe darüber. Verschieben Sie die Steigung, verschiebt sich die Berührung von einer Ecke zur nächsten — über den Punkt dazwischen springt sie hinweg. Die waagerechte Linie zeigt den Ausweg, um den es in <a href="#sec:mehrziel-epsilon">Abschnitt 14.7</a> geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.</p>
<blockquote>
<p><strong>⚠️ Der Grund liegt in der Ganzzahligkeit</strong></p>
<p>Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen <strong>ganzzahlig</strong> werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.</p>
<p>Bei einem reinen <a href="anhang-glossar.md#gloss:lineare-programmierung-lp" class="glossar-link">LP</a> ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig auf ihrer eigenen konvexen Hülle — dort findet die gewichtete Summe alles. Sobald Entscheidungen <strong>ganzzahlig</strong> werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.</p>
<p>Deshalb betrifft dieses Problem praktisch jedes betriebliche Mehrzielmodell. Wer mit Gewichten arbeitet, verliert nicht ein paar Nachkommastellen, sondern <strong>ganze Alternativen</strong> — im Beispiel 40 % der Front.</p>
</blockquote>
<h3 id="das-gewicht-ist-keine-feineinstellung">Das Gewicht ist keine Feineinstellung</h3>
@ -717,7 +717,7 @@ e(x) \le \varepsilon
\text{Schattenpreis`\index{Schattenpreis}`{=latex}} = \frac{\text{Aufpreis gegenüber dem Kostenminimum}}
{\text{eingesparte Kilogramm}}
</span></p>
<p>Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der Schattenpreis darunter, ist die Entscheidung kaufmännisch schon getroffen.</p>
<p>Diese Zahl ist mit etwas vergleichbar, das das Unternehmen ohnehin kennt: dem internen CO₂-Preis oder dem Zertifikatspreis. Liegt der <a href="anhang-glossar.md#gloss:schattenpreis" class="glossar-link">Schattenpreis</a> darunter, ist die Entscheidung kaufmännisch schon getroffen.</p>
<blockquote>
<p><strong>💻 Code-Durchgang: die drei Bausteine</strong></p>
<table>
@ -756,7 +756,7 @@ e(x) \le \varepsilon
<p><strong>Aufgabe 14.1 ⭐ — Dominanz prüfen.</strong> Plan P kostet 14 000 € und stößt 6 000 kg aus, Plan Q kostet 13 800 € und stößt 6 100 kg aus. Dominiert einer den anderen? Und wie steht es mit R (14 000 €, 6 100 kg)?</p>
<p><strong>Aufgabe 14.2 ⭐ — Der Schattenpreis in der Praxis.</strong> Der interne CO₂-Preis des Unternehmens beträgt 0,90 € je Kilogramm. Welchen Punkt der Front würden Sie empfehlen — und wie begründen Sie ihn in einem Satz?</p>
<p><strong>Aufgabe 14.3 ⭐⭐ — Mehr Trassen.</strong> Erhöhen Sie <code>BAHN_TRASSEN</code> von 5 auf 8. Was passiert mit der Länge der Front und mit der Zahl der unerreichbaren Punkte? Erklären Sie den Zusammenhang.</p>
<p><strong>Aufgabe 14.4 ⭐⭐ — Die Front der Relaxation.</strong> Lassen Sie <code>integrality</code> weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?</p>
<p><strong>Aufgabe 14.4 ⭐⭐ — Die Front der <a href="anhang-glossar.md#gloss:relaxation" class="glossar-link">Relaxation</a>.</strong> Lassen Sie <code>integrality</code> weg, sodass Anteile erlaubt sind. Berechnen Sie die Front erneut (mit einem ε-Raster, da es nun unendlich viele Punkte gibt). Wie viele Punkte liegen jetzt oberhalb der konvexen Hülle — und warum?</p>
<p><strong>Aufgabe 14.5 ⭐⭐⭐ — Drei Ziele.</strong> Ergänzen Sie die Laufzeit als drittes Ziel (LKW schnell, Bahn langsam). Die Front wird zu einer Fläche. Wie ändert sich das ε-Verfahren, und warum wächst der Aufwand so schnell?</p>
<hr />
<h2 id="sec:mehrziel-denkfehler">14.10 Finde den Denkfehler</h2>
@ -786,7 +786,7 @@ e(x) \le \varepsilon
<h2 id="sec:mehrziel-selbsttest">14.12 Selbsttest</h2>
<ol type="1">
<li>Erklären Sie Pareto-Dominanz an einem Beispiel aus Ihrem Arbeitsumfeld.</li>
<li>Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem MILP nicht?</li>
<li>Warum ist „gewichtete Summe” bei einem reinen LP unproblematisch und bei einem <a href="anhang-glossar.md#gloss:gemischt-ganzzahlige-optimierung-milp" class="glossar-link">MILP</a> nicht?</li>
<li>Ein Kollege sagt: „Ich nehme <span class="math inline">w = 0{,}5</span>, das ist neutral.” Was ist daran falsch?</li>
<li>Beschreiben Sie das ε-Constraint-Verfahren in drei Sätzen, ohne Formeln.</li>
<li>Sie legen einer Geschäftsführung eine Front mit zwölf Punkten vor. Welche Spalte brauchen Sie, damit die Entscheidung fallen kann?</li>