Dokumentation nachgezogen - und dabei den neunten Fund gemacht

Der Leser-Wegweiser Operations_Research_mit_Python_Version_04/README.md, die
Datei, die Leser als erstes oeffnen, beschrieb noch die Struktur der
Version 03:

* 12 der 15 Kapitelnummern falsch. "20_Lineare_Programmierung.md | 4" - es ist
  Kapitel 5. Bei den Finanzkapiteln lag die Abweichung bei sieben: README 11,
  tatsaechlich 18.
* 8 der 23 Kapitel fehlten ganz (Metaheuristiken, Spaltengenerierung,
  Mehrziel, Predict-then-Optimize, Testing und drei weitere).
* 93_Anhang_Spickzettel.md fehlte in der Anhangtabelle; der Bauabschnitt nannte
  dreimal build_version_03.py statt _04 und beschrieb --html als
  "Single-Page-HTML", obwohl es laengst eine mehrseitige Website erzeugt.

Dieselbe Regel wie acht Mal zuvor, dieselbe Behandlung: Die Tabelle wird
erzeugt, nicht korrigiert. schreibe_kapiteltabelle() ersetzt den Block
zwischen den KAPITELTABELLE-Marken aus der Struktur, die
baue_seiten_registry() ohnehin ermittelt - derselben Quelle, aus der die
Nummern im Buch stammen. --check meldet einen veralteten Block und bricht ab.
Gegenprobe: alle 37 Dateien aus DATEIEN stehen in der Uebersicht.

Weiter nachgezogen:

* CLAUDE.md kannte veroeffentliche_04.py nicht und nannte .env.beispiel nicht.
  Der Abschnitt "Veroeffentlichung" beschrieb noch das Kopieren von Hand samt
  curl-Kontrolle - das erledigt jetzt das Skript.
* Fundzahl der Nummern-Regel: CLAUDE.md sagte sieben, README acht, PROGRESS
  dokumentiert einen "Achter Fund". Jetzt ueberall neun.
* Diagrammzahl: CLAUDE.md zaehlt 32 Kapiteldiagramme, README 33 - der
  Unterschied ist titelseite.svg. Das steht jetzt dabei.

Nebenbei am Upload-Skript: rsync bekommt --checksum. Der Bau schreibt alle 42
Seiten bei jedem Lauf neu, auch ohne inhaltliche Aenderung; nach Zeitstempel
und Groesse waeren das jedes Mal rund 19 MB sinnlose Uebertragung. Gemessen
nach einem Neubau ohne Aenderung: 0 statt 151 Dateien.

Website unveraendert - kein Upload noetig, der Probelauf meldet 0 zu
uebertragen und 0 zu loeschen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dschlueter 2026-09-08 19:13:34 +02:00
commit 3d85488d7c
6 changed files with 212 additions and 42 deletions

View file

@ -29,63 +29,81 @@ arbeiten, üben und eigene Anwendungen bauen können.**
Der Text ist auf mehrere Dateien verteilt, damit er handhabbar bleibt. Die Nummern-Präfixe
legen die Lesereihenfolge fest.
<!-- KAPITELTABELLE:ANFANG - erzeugt von build_version_04.py -->
### Vorspann
| Datei | Inhalt |
| --- | --- |
| [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_und_Lesehilfe.md) | Vorwort, Lernpfade, Verzeichnis der Beispielprogramme, Setup, wie die Lernelemente funktionieren |
| [`01_Notation_und_Abkuerzungen.md`](01_Notation_und_Abkuerzungen.md) | Alle Symbole und Abkürzungen, ausgeschrieben und erklärt |
| [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_und_Lesehilfe.md) | Vorwort & Lesehilfe |
| [`01_Notation_und_Abkuerzungen.md`](01_Notation_und_Abkuerzungen.md) | Notation & Abkürzungen |
### Teil I — Grundlagen
### Teil I: Grundlagen des Operations Research
| Datei | Kapitel |
| --- | --- |
| [`10_Einfuehrung_OR.md`](10_Einfuehrung_OR.md) | 1 — Einführung in Operations Research |
| [`11_Mathematisches_Fundament.md`](11_Mathematisches_Fundament.md) | 2 — Vektoren, Matrizen, Konvexität |
| [`12_Python_Oekosystem.md`](12_Python_Oekosystem.md) | 3 — Das Python-Ökosystem für OR |
| [`10_Einfuehrung_OR.md`](10_Einfuehrung_OR.md) | Kapitel 1 — Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung |
| [`11_Mathematisches_Fundament.md`](11_Mathematisches_Fundament.md) | Kapitel 2 — Das mathematische Fundament — Vektoren, Matrizen, Konvexität |
| [`12_Python_Oekosystem.md`](12_Python_Oekosystem.md) | Kapitel 3 — Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten |
| [`13_Vom_Wunsch_zum_Modell.md`](13_Vom_Wunsch_zum_Modell.md) | Kapitel 4 — Vom Management-Wunsch zum Modell |
| [`19_Synthese_Grundlagen.md`](19_Synthese_Grundlagen.md) | Synthese Teil I |
### Teil II — Deterministische Kernverfahren
### Teil II: Die Kernverfahren der deterministischen Optimierung
| Datei | Kapitel |
| --- | --- |
| [`20_Lineare_Programmierung.md`](20_Lineare_Programmierung.md) | 4 — Lineare Programmierung, Simplex, Dualität |
| [`21_MILP.md`](21_MILP.md) | 5 — Gemischt-ganzzahlige Optimierung |
| [`22_CP_SAT.md`](22_CP_SAT.md) | 6 — Constraint Programming mit CP-SAT |
| [`23_Graphen_Fluesse_Touren.md`](23_Graphen_Fluesse_Touren.md) | 7 — Graphen, Flüsse, Tourenplanung |
| [`20_Lineare_Programmierung.md`](20_Lineare_Programmierung.md) | Kapitel 5 — Lineare Programmierung — Simplex, Dualität und Schattenpreise |
| [`21_MILP.md`](21_MILP.md) | Kapitel 6 — Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound |
| [`22_CP_SAT.md`](22_CP_SAT.md) | Kapitel 7 — Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung |
| [`23_Graphen_Fluesse_Touren.md`](23_Graphen_Fluesse_Touren.md) | Kapitel 8 — Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP |
| [`24_Metaheuristiken.md`](24_Metaheuristiken.md) | Kapitel 9 — Metaheuristiken — wenn der exakte Solver aussteigt |
| [`25_Spaltengenerierung.md`](25_Spaltengenerierung.md) | Kapitel 10 — Spaltengenerierung — das Modell umbauen statt die Lösung raten |
| [`29_Synthese_Kernverfahren.md`](29_Synthese_Kernverfahren.md) | Synthese Teil II |
### Teil III — Nichtlinearität, Unsicherheit, Dynamik
### Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik
| Datei | Kapitel |
| --- | --- |
| [`30_QP_und_NLP.md`](30_QP_und_NLP.md) | 8 — Quadratische und nichtlineare Optimierung, KKT |
| [`31_Unsicherheit.md`](31_Unsicherheit.md) | 9 — Monte-Carlo, stochastische und robuste Optimierung |
| [`32_Dynamische_Programmierung.md`](32_Dynamische_Programmierung.md) | 10 — Dynamische Programmierung, Bellman |
| [`30_QP_und_NLP.md`](30_QP_und_NLP.md) | Kapitel 11 — Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität |
| [`31_Unsicherheit.md`](31_Unsicherheit.md) | Kapitel 12 — Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit |
| [`32_Dynamische_Programmierung.md`](32_Dynamische_Programmierung.md) | Kapitel 13 — Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution |
| [`33_Mehrziel_und_Pareto.md`](33_Mehrziel_und_Pareto.md) | Kapitel 14 — Mehrere Ziele — Pareto-Fronten statt Gewichte |
| [`34_Predict_then_Optimize.md`](34_Predict_then_Optimize.md) | Kapitel 15 — Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung |
| [`39_Synthese_Nichtlinear.md`](39_Synthese_Nichtlinear.md) | Synthese Teil III |
### Teil IV — Operations Research an den Finanzmärkten
### Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte
| Datei | Kapitel |
| --- | --- |
| [`40_Finanzdaten.md`](40_Finanzdaten.md) | 11 — Finanzdaten, Kovarianz, Shrinkage |
| [`41_Markowitz.md`](41_Markowitz.md) | 12 — Markowitz-Portfoliotheorie |
| [`42_CVaR.md`](42_CVaR.md) | 13 — Tail-Risiko, CVaR, Transaktionskosten |
| [`43_Handelsmaschine.md`](43_Handelsmaschine.md) | 14 — Die vollständige Handelsmaschine |
| [`35_Strukturbruecke.md`](35_Strukturbruecke.md) | Kapitel 16 — Die Strukturbrücke — dieselbe Mathematik, zwei Welten |
| [`36_Supply_Chain_Energie.md`](36_Supply_Chain_Energie.md) | Kapitel 17 — Supply-Chain und Energieeinsatz unter Unsicherheit |
| [`40_Finanzdaten.md`](40_Finanzdaten.md) | Kapitel 18 — Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage |
| [`41_Markowitz.md`](41_Markowitz.md) | Kapitel 19 — Die moderne Portfoliotheorie nach Markowitz |
| [`42_CVaR.md`](42_CVaR.md) | Kapitel 20 — Tail-Risiko, CVaR und Transaktionskosten |
| [`43_Handelsmaschine.md`](43_Handelsmaschine.md) | Kapitel 21 — Die vollständige quantitative Handelsmaschine |
| [`49_Synthese_Anwendungen.md`](49_Synthese_Anwendungen.md) | Synthese Teil IV |
### Teil V — Praxis
### Teil V: Praxis
| Datei | Kapitel |
| --- | --- |
| [`50_Praxis.md`](50_Praxis.md) | 15 — Praxisfallen und produktiver Einsatz |
| [`59_Projektwerkstatt.md`](59_Projektwerkstatt.md) | Projektwerkstatt: 8 eigene Anwendungen |
| [`50_Praxis.md`](50_Praxis.md) | Kapitel 22 — Praxisfallen und der Weg zum produktiven Einsatz |
| [`51_Testing_und_Deployment.md`](51_Testing_und_Deployment.md) | Kapitel 23 — Testen, Messen, Ausliefern |
| [`52_Synthese_Praxis.md`](52_Synthese_Praxis.md) | Synthese Teil V |
| [`59_Projektwerkstatt.md`](59_Projektwerkstatt.md) | Projektwerkstatt |
### Anhang
| Datei | Inhalt |
| --- | --- |
| [`90_Anhang_Loesungen.md`](90_Anhang_Loesungen.md) | Lösungen zu **allen** Übungsaufgaben und Selbsttests |
| [`91_Anhang_Modellierungsmuster.md`](91_Anhang_Modellierungsmuster.md) | Musterkatalog: 24 wiederkehrende Modellierungstricks |
| [`92_Anhang_Fehlerdiagnose.md`](92_Anhang_Fehlerdiagnose.md) | Diagnose-Handbuch für Solver-Probleme |
| [`94_Anhang_Glossar.md`](94_Anhang_Glossar.md) | Glossar der Fachbegriffe |
| [`95_Anhang_Literatur.md`](95_Anhang_Literatur.md) | Literaturverzeichnis |
| [`90_Anhang_Loesungen.md`](90_Anhang_Loesungen.md) | Anhang A — Lösungen zu allen Übungsaufgaben |
| [`91_Anhang_Modellierungsmuster.md`](91_Anhang_Modellierungsmuster.md) | Anhang B — Katalog der Modellierungsmuster |
| [`92_Anhang_Fehlerdiagnose.md`](92_Anhang_Fehlerdiagnose.md) | Anhang C — Fehlerdiagnose-Handbuch |
| [`93_Anhang_Spickzettel.md`](93_Anhang_Spickzettel.md) | Anhang D — Spickzettel der Solver |
| [`94_Anhang_Glossar.md`](94_Anhang_Glossar.md) | Anhang E — Glossar |
| [`95_Anhang_Literatur.md`](95_Anhang_Literatur.md) | Anhang F — Literaturverzeichnis |
<!-- KAPITELTABELLE:ENDE -->
---
@ -95,7 +113,7 @@ Wer lieber ein einziges Dokument möchte (z. B. zum Drucken oder für die PDF-Er
baut es aus den Einzeldateien:
```bash
cd OR_mit_Python/Version_04 # Wurzel der Version 04
cd Version_04 # das Wurzelverzeichnis des Repositorys
python3 Operations_Research_mit_Python_Version_04/build_version_04.py
```
@ -105,11 +123,15 @@ Inhaltsverzeichnis-Platzhalter und entfernten unnötigen Zeilenumbrüchen innerh
Absätzen. Optionen:
```bash
python3 .../build_version_03.py --pdf # zusätzlich PDF über pandoc + xelatex
python3 .../build_version_03.py --html # zusätzlich Single-Page-HTML
python3 .../build_version_03.py --check # nur Konsistenzprüfung, schreibt nichts
python3 .../build_version_04.py --pdf # zusätzlich PDF über pandoc + xelatex
python3 .../build_version_04.py --html # zusätzlich die Website OR_HTML_04/
python3 .../build_version_04.py --check # nur Konsistenzprüfung, schreibt nichts
```
`--html` erzeugt die **mehrseitige** Website mit Suche, Stichwortverzeichnis und
Notebook-Downloads; das Gesamtdokument auf einer Seite liegt darin als
`gesamtdokument.html`.
---
## Empfohlene Reihenfolge für Kursteilnehmer

View file

@ -2443,6 +2443,86 @@ def baue_stichwortverzeichnis(html_verz: str, struktur: list[dict],
print(f"Stichwortverzeichnis erzeugt: {len(namen)} Hauptbegriffe.")
# Der Leser-Wegweiser Operations_Research_mit_Python_Version_04/README.md fuehrt
# alle Quelldateien mit ihrer Kapitelnummer auf. Diese Tabelle stand von Hand
# darin - und war der NEUNTE Fall der Regel "keine abgeleiteten Zahlen im
# Quelltext": 12 der 15 Nummern zeigten noch die Zaehlung der Version 03, und
# 8 der 23 Kapitel fehlten ganz. Jetzt erzeugt der Bau den Block zwischen den
# beiden Marken; --check meldet, wenn er veraltet ist.
README_MARKE_ANFANG = "<!-- KAPITELTABELLE:ANFANG - erzeugt von build_version_04.py -->"
README_MARKE_ENDE = "<!-- KAPITELTABELLE:ENDE -->"
LESER_README = os.path.join(HIER, "README.md")
def baue_kapiteltabelle(struktur: list[dict]) -> str:
"""Baut die Dateiuebersicht des Leser-Wegweisers aus der Struktur, die
baue_seiten_registry() ohnehin ermittelt - dieselbe Quelle, aus der auch
die Nummern im Buch stammen. Damit koennen die beiden nicht auseinander-
laufen."""
zeilen = [README_MARKE_ANFANG, ""]
def tabelle(eintraege: list[dict], spalte: str) -> None:
zeilen.append(f"| Datei | {spalte} |")
zeilen.append("| --- | --- |")
for e in eintraege:
was = f'{e["nummer"]}{e["titel"]}' if e["nummer"] else e["titel"]
zeilen.append(f'| [`{e["datei"]}`]({e["datei"]}) | {was} |')
zeilen.append("")
vorspann = [e for e in struktur if e["teil"] is None and e["typ"] == "sonder"]
if vorspann:
zeilen.append("### Vorspann")
zeilen.append("")
tabelle(vorspann, "Inhalt")
gesehen: list[str] = []
for e in struktur:
if e["teil"] and e["teil"] not in gesehen:
gesehen.append(e["teil"])
for teil in gesehen:
zeilen.append(f"### {teil}")
zeilen.append("")
tabelle([e for e in struktur if e["teil"] == teil], "Kapitel")
anhaenge = [e for e in struktur if e["typ"] == "anhang"]
if anhaenge:
zeilen.append("### Anhang")
zeilen.append("")
tabelle(anhaenge, "Inhalt")
zeilen.append(README_MARKE_ENDE)
return "\n".join(zeilen)
def schreibe_kapiteltabelle(struktur: list[dict], nur_pruefen: bool) -> list[str]:
"""Ersetzt den markierten Block im Leser-Wegweiser. Liefert bei --check
eine nicht leere Liste, wenn er veraltet ist."""
if not os.path.exists(LESER_README):
return []
with open(LESER_README, encoding="utf-8") as f:
inhalt = f.read()
if README_MARKE_ANFANG not in inhalt or README_MARKE_ENDE not in inhalt:
print(f"FEHLER: {os.path.basename(LESER_README)} hat keine "
f"KAPITELTABELLE-Marken - die Dateiuebersicht kann nicht "
f"erzeugt werden.")
return ["README-Marken"]
vorher, rest = inhalt.split(README_MARKE_ANFANG, 1)
_, nachher = rest.split(README_MARKE_ENDE, 1)
neu = vorher + baue_kapiteltabelle(struktur) + nachher
if neu == inhalt:
return []
if nur_pruefen:
print(f"FEHLER: Die Dateiuebersicht in "
f"{os.path.basename(LESER_README)} ist veraltet - ein Lauf ohne "
f"--check zieht sie nach.")
return ["README-Kapiteltabelle"]
with open(LESER_README, "w", encoding="utf-8") as f:
f.write(neu)
print(f"Dateiuebersicht im Leser-Wegweiser nachgezogen.")
return []
def erzeuge_suchindex(html_verz: str, suchindex: list[dict]) -> None:
# .js mit globaler Variable statt .json + fetch() - siehe Kommentar in
# baue_programme_seite() (fetch() auf lokale Dateien scheitert unter
@ -2511,6 +2591,13 @@ def main() -> int:
print(f"Geprueft: {len(DATEIEN)} Dateien, {zeilen:,} Zeilen, "
f"{zeichen/1024:.0f} KB, {kapitel} Hauptueberschriften.")
# Die Dateiuebersicht des Leser-Wegweisers stammt aus derselben Struktur
# wie die Nummern im Buch. Bei --check wird nur gemeldet, dass sie veraltet
# ist; geschrieben wird sie im normalen Lauf.
_, _, _, struktur_fuer_readme, _ = baue_seiten_registry()
if schreibe_kapiteltabelle(struktur_fuer_readme, nur_pruefen=argumente.check):
return 1
if argumente.check:
print("Nur Pruefung - es wurde nichts geschrieben.")
return 0

View file

@ -192,7 +192,13 @@ def rsync_befehl(env: dict[str, str], probelauf: bool) -> list[str]:
# ohnehin nicht setzen, -a erzeugte dafuer nur Warnungen. --chmod sorgt
# dafuer, dass der Webserver alles lesen kann, unabhaengig von der lokalen
# umask.
befehl = ["rsync", "-rltz", "--delete", "--chmod=D755,F644",
# --checksum: vergleiche INHALTE, nicht Groesse und Zeitstempel. Der Bau
# schreibt alle 42 Seiten bei jedem Lauf neu, auch wenn sich nichts
# geaendert hat - nach der Vorgabe waeren das jedes Mal rund 19 MB
# sinnlose Uebertragung. Mit --checksum meldet der Probelauf genau die
# Dateien, deren Inhalt sich wirklich unterscheidet. Das Lesen der 23 MB
# auf beiden Seiten faellt dagegen nicht ins Gewicht.
befehl = ["rsync", "-rltz", "--checksum", "--delete", "--chmod=D755,F644",
"--skip-compress=pdf/zip/woff2/png/jpg/svg",
"--itemize-changes",
"-e", ssh_optionen(env)]