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

@ -129,7 +129,7 @@
</tr>
<tr class="even">
<td>B1</td>
<td>Aktivierungsschalter</td>
<td><a href="anhang-glossar.md#gloss:aktivierungsschalter" class="glossar-link">Aktivierungsschalter</a></td>
<td>„Wenn genutzt, dann Fixkosten“</td>
<td><a href="milp.html#kap-milp">Kapitel 6</a></td>
</tr>
@ -141,7 +141,7 @@
</tr>
<tr class="even">
<td>B3</td>
<td>Implikation</td>
<td><a href="anhang-glossar.md#gloss:implikation" class="glossar-link">Implikation</a></td>
<td>„Wenn A, dann auch B“</td>
<td><a href="milp.html#kap-milp">Kapitel 6</a></td>
</tr>
@ -153,7 +153,7 @@
</tr>
<tr class="even">
<td>B5</td>
<td>Exklusiv-Oder</td>
<td><a href="anhang-glossar.md#gloss:exklusiv-oder" class="glossar-link">Exklusiv-Oder</a></td>
<td>„Genau eines von N“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
@ -165,7 +165,7 @@
</tr>
<tr class="even">
<td>B7</td>
<td>Bedingte Kopplung</td>
<td><a href="anhang-glossar.md#gloss:bedingte-kopplung" class="glossar-link">Bedingte Kopplung</a></td>
<td>„Wenn A und B, dann C“</td>
<td><a href="milp.html#kap-milp">Kapitel 6</a></td>
</tr>
@ -183,25 +183,25 @@
</tr>
<tr class="odd">
<td>B9</td>
<td>Gestaffelte Preise</td>
<td><a href="anhang-glossar.md#gloss:gestaffelte-preise" class="glossar-link">Gestaffelte Preise</a></td>
<td>„Erste 100 Stück billiger“</td>
<td></td>
</tr>
<tr class="even">
<td>B10</td>
<td>Absolutbetrag</td>
<td><a href="anhang-glossar.md#gloss:absolutbetrag" class="glossar-link">Absolutbetrag</a></td>
<td>„Abweichung nach oben wie unten“</td>
<td><a href="cvar.html#kap-cvar">Kapitel 20</a></td>
</tr>
<tr class="odd">
<td>B11</td>
<td>Min/Max in der Zielfunktion</td>
<td>Min/Max in der <a href="anhang-glossar.md#gloss:zielfunktion" class="glossar-link">Zielfunktion</a></td>
<td>„Den Schlechtesten verbessern“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="even">
<td>B12</td>
<td>Verhältnis-Bedingung</td>
<td><a href="anhang-glossar.md#gloss:verhaeltnis-bedingung" class="glossar-link">Verhältnis-Bedingung</a></td>
<td>„Anteil mindestens 30 %“</td>
<td><a href="markowitz.html#kap-markowitz">Kapitel 19</a></td>
</tr>
@ -213,7 +213,7 @@
</tr>
<tr class="even">
<td>B27</td>
<td>Budgetlimit</td>
<td><a href="anhang-glossar.md#gloss:budgetlimit" class="glossar-link">Budgetlimit</a></td>
<td>„Mehr als 2 Mio. gibt es nicht“</td>
<td><a href="milp.html#kap-milp">Kapitel 6</a></td>
</tr>
@ -225,37 +225,37 @@
</tr>
<tr class="even">
<td>B13</td>
<td>Vorrangbeziehung</td>
<td><a href="anhang-glossar.md#gloss:vorrangbeziehung" class="glossar-link">Vorrangbeziehung</a></td>
<td>„B erst nach A“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="odd">
<td>B14</td>
<td>Nichtüberlappung</td>
<td><a href="anhang-glossar.md#gloss:nichtueberlappung" class="glossar-link">Nichtüberlappung</a></td>
<td>„Eine Maschine, ein Job“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="even">
<td>B15</td>
<td>Kumulative Ressource</td>
<td><a href="anhang-glossar.md#gloss:kumulative-ressource" class="glossar-link">Kumulative Ressource</a></td>
<td>„Höchstens 3 gleichzeitig“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="odd">
<td>B16</td>
<td>Gleitendes Fenster</td>
<td><a href="anhang-glossar.md#gloss:gleitendes-fenster" class="glossar-link">Gleitendes Fenster</a></td>
<td>„Höchstens 5 Tage in Folge“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="even">
<td>B17</td>
<td>Umrüstkosten</td>
<td><a href="anhang-glossar.md#gloss:umruestkosten" class="glossar-link">Umrüstkosten</a></td>
<td>„Wechsel kostet extra“</td>
<td><a href="cpsat.html#kap-cpsat">Kapitel 7</a></td>
</tr>
<tr class="odd">
<td>B26</td>
<td>Rüstzeit als Kapazität</td>
<td><a href="anhang-glossar.md#gloss:ruestzeit" class="glossar-link">Rüstzeit</a> als Kapazität</td>
<td>„Umbauen kostet Maschinenstunden“</td>
<td><a href="supplychain.html#kap-supplychain">Kapitel 17</a></td>
</tr>
@ -279,14 +279,14 @@
</tr>
<tr class="odd">
<td>B20</td>
<td>Symmetriebrechung</td>
<td><a href="anhang-glossar.md#gloss:symmetriebrechung" class="glossar-link">Symmetriebrechung</a></td>
<td>„Gleiche Objekte nicht doppelt zählen“</td>
<td><a href="dekomposition.html#kap-dekomposition">Kapitel 10</a></td>
</tr>
<tr class="even">
<td>B21</td>
<td>Worst-Case-Abzug</td>
<td>„Gegen Schätzfehler absichern“</td>
<td>„Gegen <a href="anhang-glossar.md#gloss:schaetzfehler" class="glossar-link">Schätzfehler</a> absichern“</td>
<td><a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a></td>
</tr>
<tr class="odd">
@ -297,7 +297,7 @@
</tr>
<tr class="even">
<td>B22</td>
<td>Flusserhaltung</td>
<td><a href="anhang-glossar.md#gloss:flusserhaltung" class="glossar-link">Flusserhaltung</a></td>
<td>„Was reinkommt, geht raus“</td>
<td><a href="graphen.html#kap-graphen">Kapitel 8</a></td>
</tr>
@ -309,7 +309,7 @@
</tr>
<tr class="even">
<td>B24</td>
<td>Subtour-Eliminierung</td>
<td><a href="anhang-glossar.md#gloss:subtour" class="glossar-link">Subtour</a>-Eliminierung</td>
<td>„Keine isolierten Kreise“</td>
<td><a href="graphen.html#kap-graphen">Kapitel 8</a></td>
</tr>
@ -327,7 +327,7 @@
<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>modell.Add(x <span class="op">&lt;=</span> M <span class="op">*</span> y) <span class="co"># CP-SAT</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co"># LP/MILP: Zeile x - M*y &lt;= 0</span></span></code></pre></div>
<blockquote>
<p>⚠️ <strong><span class="math inline">M</span> so klein wie möglich</strong> — idealerweise die ohnehin vorhandene Kapazitätsgrenze von <span class="math inline">x</span>. Zu großes <span class="math inline">M</span> macht die LP-Relaxation wertlos (<a href="milp.html#sec:milp-modellierungstricks-big-m-und-logische-bedingungen">Abschnitt 6.5</a>).</p>
<p>⚠️ <strong><span class="math inline">M</span> so klein wie möglich</strong> — idealerweise die ohnehin vorhandene Kapazitätsgrenze von <span class="math inline">x</span>. Zu großes <span class="math inline">M</span> macht die <a href="anhang-glossar.md#gloss:lp-relaxation" class="glossar-link">LP-Relaxation</a> wertlos (<a href="milp.html#sec:milp-modellierungstricks-big-m-und-logische-bedingungen">Abschnitt 6.5</a>).</p>
</blockquote>
<h3 id="b2-semikontinuierliche-variable">B2 — Semikontinuierliche Variable</h3>
<p><strong>Regel.</strong> „Entweder gar nicht oder mindestens <span class="math inline">L</span> (und höchstens <span class="math inline">U</span>).“</p>
@ -400,19 +400,19 @@
<p>⚠️ <strong>Die häufigste Verwechslung:</strong> <span class="math inline">L\,y_t \le x_t</span> <strong>je Periode</strong> (B2) ist ein <em>anderes</em> Modell — es verlangt in jeder einzelnen Periode eine Mindestmenge und ist erheblich strenger. Wer den Jahresvertrag so formuliert, erzeugt ein unlösbares Modell und sucht den Fehler dann in den Daten.</p>
</blockquote>
<blockquote>
<p>⚠️ <strong>Die obere Kopplung nicht vergessen.</strong> Ohne <span class="math inline">\sum_t x_t \le U\,y</span> kann das Modell <span class="math inline">y = 0</span> setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der <em>Trickle Flow</em> aus <a href="milp.html#sec:milp-modellierungstricks-big-m-und-logische-bedingungen">Abschnitt 6.5</a>, nur andersherum. <span class="math inline">U</span> ist die Jahreskapazität des Lieferanten, keine runde Zahl.</p>
<p>⚠️ <strong>Die obere Kopplung nicht vergessen.</strong> Ohne <span class="math inline">\sum_t x_t \le U\,y</span> kann das Modell <span class="math inline">y = 0</span> setzen und trotzdem einkaufen — der Vertrag gilt dann als nicht geschlossen, die Ware fließt aber. Das ist der <em><a href="anhang-glossar.md#gloss:trickle-flow" class="glossar-link">Trickle Flow</a></em> aus <a href="milp.html#sec:milp-modellierungstricks-big-m-und-logische-bedingungen">Abschnitt 6.5</a>, nur andersherum. <span class="math inline">U</span> ist die Jahreskapazität des Lieferanten, keine runde Zahl.</p>
</blockquote>
<h3 id="b27-budgetlimit">B27 — Budgetlimit</h3>
<p><strong>Regel.</strong> „Alle Maßnahmen zusammen dürfen 2 Mio. € nicht überschreiten.“</p>
<p><span class="math display">\sum_i c_i x_i \le B</span></p>
<p>Mit <span class="math inline">x_i \in \{0,1\}</span> ist das ein <strong>Rucksackproblem</strong> — dasselbe Muster, das <a href="milp.html#kap-milp">Kapitel 6</a> an <code>Rucksack.py</code> vorrechnet und <a href="dekomposition.html#kap-dekomposition">Kapitel 10</a> als Pricing-Teilproblem wiederverwendet.</p>
<p>Mit <span class="math inline">x_i \in \{0,1\}</span> ist das ein <strong><a href="anhang-glossar.md#gloss:rucksackproblem" class="glossar-link">Rucksackproblem</a></strong> — dasselbe Muster, das <a href="milp.html#kap-milp">Kapitel 6</a> an <code>Rucksack.py</code> vorrechnet und <a href="dekomposition.html#kap-dekomposition">Kapitel 10</a> als Pricing-Teilproblem wiederverwendet.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>modell.Add(<span class="bu">sum</span>(KOSTEN[i] <span class="op">*</span> x[i] <span class="cf">for</span> i <span class="kw">in</span> massnahmen) <span class="op">&lt;=</span> BUDGET)</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="co"># Mehrere Toepfe: je Topf eine Zeile - NICHT die Summe ueber alle Toepfe</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> topf, grenze <span class="kw">in</span> BUDGETS.items():</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> modell.Add(<span class="bu">sum</span>(KOSTEN[i] <span class="op">*</span> x[i] <span class="cf">for</span> i <span class="kw">in</span> massnahmen</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> TOPF[i] <span class="op">==</span> topf) <span class="op">&lt;=</span> grenze)</span></code></pre></div>
<blockquote>
<p>💡 <strong>Der Schattenpreis des Budgets ist die Zahl, nach der die Geschäftsführung fragt:</strong> „Was bringt der nächste Euro?“ Bei einem <strong>LP</strong> ist er direkt ablesbar. Bei <strong>Ganzzahligkeit gibt es ihn nicht</strong> — die Dualwerte der Relaxation sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (<a href="lp.html#sec:lp-entartung">Abschnitt 5.9</a>).</p>
<p>💡 <strong>Der <a href="anhang-glossar.md#gloss:schattenpreis" class="glossar-link">Schattenpreis</a> des Budgets ist die Zahl, nach der die Geschäftsführung fragt:</strong> „Was bringt der nächste Euro?“ Bei einem <strong><a href="anhang-glossar.md#gloss:lineare-programmierung-lp" class="glossar-link">LP</a></strong> ist er direkt ablesbar. Bei <strong>Ganzzahligkeit gibt es ihn nicht</strong> — die Dualwerte der <a href="anhang-glossar.md#gloss:relaxation" class="glossar-link">Relaxation</a> sind keine gültige Antwort. Rechnen Sie stattdessen mit erhöhtem Budget neu und vergleichen Sie die Zielwerte (<a href="lp.html#sec:lp-entartung">Abschnitt 5.9</a>).</p>
</blockquote>
<blockquote>
<p>⚠️ <strong>Die LP-Relaxation verspricht zu viel.</strong> Sie darf die letzte Maßnahme anteilig kaufen und liefert deshalb eine Schranke, die spürbar über dem tatsächlich Erreichbaren liegen kann. Wer sie als Prognose berichtet, verspricht Geld, das nicht kommt.</p>
@ -426,7 +426,7 @@
<h3 id="b14-nichtüberlappung">B14 — Nichtüberlappung</h3>
<p><strong>Regel.</strong> „Eine Maschine bearbeitet nur einen Job gleichzeitig.“</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>modell.AddNoOverlap([intervall_1, intervall_2, ...])</span></code></pre></div>
<p>In MILP bräuchte man je Paar eine Disjunktion (B4) — bei <span class="math inline">k</span> Jobs sind das <span class="math inline">\binom{k}{2}</span> Konstruktionen. <strong>Nehmen Sie hier CP-SAT.</strong></p>
<p>In <a href="anhang-glossar.md#gloss:gemischt-ganzzahlige-optimierung-milp" class="glossar-link">MILP</a> bräuchte man je Paar eine Disjunktion (B4) — bei <span class="math inline">k</span> Jobs sind das <span class="math inline">\binom{k}{2}</span> Konstruktionen. <strong>Nehmen Sie hier <a href="anhang-glossar.md#gloss:cp-sat" class="glossar-link">CP-SAT</a>.</strong></p>
<h3 id="b15-kumulative-ressource">B15 — Kumulative Ressource</h3>
<p><strong>Regel.</strong> „Zu keinem Zeitpunkt dürfen mehr als 3 Arbeiten gleichzeitig laufen“ bzw. „die Stromlast darf 500 kW nie übersteigen“.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>modell.AddCumulative(intervalle, bedarfe, kapazitaet)</span></code></pre></div>
@ -458,7 +458,7 @@
<p><strong>Das wichtigste Muster für den Produktivbetrieb.</strong></p>
<p><strong>Regel.</strong> „Diese Bedingung soll gelten — aber lieber ein schlechter Plan als gar keiner.“</p>
<p><span class="math display">\sum_p x_{p,s} + u_s = 1,\qquad u_s\in\{0,1\}</span> Zielfunktion: <span class="math inline">\dots + 10\,000\cdot u_s</span></p>
<p>Die Strafe muss <strong>hoch genug</strong> sein, dass der Solver sie nur im Notfall in Kauf nimmt, aber <strong>endlich</strong>, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.</p>
<p>Die Strafe muss <strong>hoch genug</strong> sein, dass der <a href="anhang-glossar.md#gloss:solver" class="glossar-link">Solver</a> sie nur im Notfall in Kauf nimmt, aber <strong>endlich</strong>, damit es überhaupt eine Lösung gibt. Faustregel: eine Größenordnung über der Summe aller weichen Ziele.</p>
<h3 id="b19-hierarchische-ziele-lexikografisch">B19 — Hierarchische Ziele (lexikografisch)</h3>
<p><strong>Regel.</strong> „Erst die Besetzung sicherstellen, dann die Fairness optimieren.“</p>
<p><strong>Variante A — Gewichtung:</strong> Strafen um Größenordnungen staffeln (<span class="math inline">10\,000 \gg 100 \gg 1</span>). Einfach, aber bei extremen Skalenunterschieden numerisch heikel.</p>
@ -475,7 +475,7 @@
<h3 id="b20-symmetriebrechung">B20 — Symmetriebrechung</h3>
<p><strong>Regel.</strong> „Drei identische Maschinen — der Solver soll nicht alle Vertauschungen durchprobieren.“</p>
<p><span class="math display">\text{start}_1 \le \text{start}_2 \le \text{start}_3 \qquad\text{bzw.}\qquad \sum_j x_{1j} \ge \sum_j x_{2j}</span></p>
<p>Ohne Symmetriebrechung durchsucht Branch-and-Bound <span class="math inline">k!</span> gleichwertige Lösungen. <strong>Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.</strong></p>
<p>Ohne Symmetriebrechung durchsucht <a href="anhang-glossar.md#gloss:branch-and-bound" class="glossar-link">Branch-and-Bound</a> <span class="math inline">k!</span> gleichwertige Lösungen. <strong>Eine einzige Ordnungsbedingung kann die Laufzeit um Größenordnungen senken.</strong></p>
<h3 id="b21-worst-case-abzug-robuste-formulierung">B21 — Worst-Case-Abzug (robuste Formulierung)</h3>
<p><strong>Regel.</strong> „Rechne nicht mit dem geschätzten Wert, sondern mit dem ungünstigsten plausiblen.“</p>
<p>Bei Box-Unsicherheit <span class="math inline">\mu_i \in [\hat\mu_i - \delta_i, \hat\mu_i + \delta_i]</span> und <span class="math inline">w \ge 0</span>: <span class="math display">\hat{\boldsymbol{\mu}}^\top\mathbf{w} \;\longrightarrow\; \hat{\boldsymbol{\mu}}^\top\mathbf{w} - \boldsymbol{\delta}^\top\mathbf{w}</span></p>
@ -490,11 +490,11 @@
<p></p>
<p><span class="math display">\sum_j x_{ij} = 1\ \forall i,\qquad \sum_i x_{ij} = 1\ \forall j,\qquad x_{ij} \ge 0</span></p>
<blockquote>
<p>💡 <strong>Ganzzahligkeit nicht fordern!</strong> Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist <code>scipy.optimize.linear_sum_assignment</code> (Ungarischer Algorithmus, <span class="math inline">O(n^3)</span>) noch deutlich schneller.</p>
<p>💡 <strong>Ganzzahligkeit nicht fordern!</strong> Die Matrix ist total unimodular; ein LP-Solver liefert automatisch 0/1-Lösungen. Für reine Zuordnungen ist <code>scipy.optimize.linear_sum_assignment</code> (<a href="anhang-glossar.md#gloss:ungarischer-algorithmus" class="glossar-link">Ungarischer Algorithmus</a>, <span class="math inline">O(n^3)</span>) noch deutlich schneller.</p>
</blockquote>
<h3 id="b24-subtour-eliminierung">B24 — Subtour-Eliminierung</h3>
<p><strong>MTZ (einfach, aber schwach):</strong> <span class="math display">u_i - u_j + C x_{ij} \le C - d_j \qquad \forall i\ne j</span></p>
<p><strong>Besser in der Praxis:</strong> <code>AddCircuit</code> in CP-SAT oder die Routing-Bibliothek von OR-Tools.</p>
<p><strong>Besser in der Praxis:</strong> <code>AddCircuit</code> in <a href="anhang-glossar.md#gloss:constraint-programming-cp" class="glossar-link">CP</a>-<a href="anhang-glossar.md#gloss:sat-boolean-satisfiability" class="glossar-link">SAT</a> oder die <a href="anhang-glossar.md#gloss:routing-bibliothek" class="glossar-link">Routing-Bibliothek</a> von <a href="anhang-glossar.md#gloss:or-tools" class="glossar-link">OR-Tools</a>.</p>
<hr />
<h2 id="ein-wort-zur-auswahl">Ein Wort zur Auswahl</h2>
<p>Wenn mehrere Muster passen, entscheiden Sie nach dieser Reihenfolge:</p>