Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
/ * site . css — Design-System der Mehrseiten-Website " Operations Research mit
Python " . Struktur ( CSS-Variablen , . dark-Umschaltung , kuehle Graustufen )
nach dem Vorbild https : / / jamulix . de / sorting / , Farben aus der bestehenden
Buchpalette ( Titelseite / PDF : Indigo / Cyan / Violett / Gruen / Amber ) . * /
: root {
--bg : # f5f6fa ;
--surface : # ffffff ;
--surface2 : # eef0f5 ;
--border : # d9dce6 ;
--text : # 1a1a2e ;
--muted : # 5c6380 ;
--accent : # 4338ca ;
--accent-text : # ffffff ;
--cyan : # 0891b2 ;
--violett : # 7c3aed ;
--emerald : # 059669 ;
--amber : # b45309 ;
--rose : # e11d48 ;
--tint-indigo : # eef2ff ;
--tint-violett : # f5f3ff ;
--tint-cyan : # ecfeff ;
--tint-emerald : # ecfdf5 ;
--tint-amber : # fffbeb ;
--code-bg : # 1e293b ;
--code-text : # e6edf3 ;
--shadow : 0 2px 10px rgba ( 26 , 26 , 46 , . 07 ) ;
--shadow-lg : 0 8px 30px rgba ( 26 , 26 , 46 , . 12 ) ;
--radius : 12px ;
}
@ media ( prefers-color-scheme : dark ) {
: root : not ( [ data-theme = "light" ] ) {
--bg : # 0e0f1a ;
--surface : # 161826 ;
--surface2 : # 1e2133 ;
--border : # 2b2f45 ;
--text : # e7e9f5 ;
--muted : # 9aa1c4 ;
--accent : # 818cf8 ;
--accent-text : # 0e0f1a ;
--cyan : # 22d3ee ;
--violett : # c4b5fd ;
--emerald : # 34d399 ;
--amber : # fbbf24 ;
--rose : # fb7185 ;
--tint-indigo : # 1b1f3a ;
--tint-violett : # 241f3d ;
--tint-cyan : # 132833 ;
--tint-emerald : # 10291f ;
--tint-amber : # 2c2410 ;
--code-bg : # 0b0d14 ;
--code-text : # dbe4f0 ;
--shadow : 0 2px 10px rgba ( 0 , 0 , 0 , . 35 ) ;
--shadow-lg : 0 8px 30px rgba ( 0 , 0 , 0 , . 5 ) ;
}
}
: root [ data-theme = "dark" ] {
--bg : # 0e0f1a ;
--surface : # 161826 ;
--surface2 : # 1e2133 ;
--border : # 2b2f45 ;
--text : # e7e9f5 ;
--muted : # 9aa1c4 ;
--accent : # 818cf8 ;
--accent-text : # 0e0f1a ;
--cyan : # 22d3ee ;
--violett : # c4b5fd ;
--emerald : # 34d399 ;
--amber : # fbbf24 ;
--rose : # fb7185 ;
--tint-indigo : # 1b1f3a ;
--tint-violett : # 241f3d ;
--tint-cyan : # 132833 ;
--tint-emerald : # 10291f ;
--tint-amber : # 2c2410 ;
--code-bg : # 0b0d14 ;
--code-text : # dbe4f0 ;
--shadow : 0 2px 10px rgba ( 0 , 0 , 0 , . 35 ) ;
--shadow-lg : 0 8px 30px rgba ( 0 , 0 , 0 , . 5 ) ;
}
* { box-sizing : border-box ; }
html { scroll-behavior : smooth ; }
body {
margin : 0 ; background : var ( --bg ) ; color : var ( --text ) ;
font-family : "Segoe UI" , -apple-system , BlinkMacSystemFont , "DejaVu Sans" , Arial , sans-serif ;
line-height : 1 . 7 ; font-size : 16px ; -webkit-text-size-adjust : 100 % ;
}
. icon { width : 1.15 em ; height : 1.15 em ; flex : none ; vertical-align : -.2 em ; }
/* --- Kopfzeile --- */
. site-header {
position : sticky ; top : 0 ; z-index : 30 ; display : flex ; align-items : center ;
gap : . 75rem ; padding : . 6rem 1rem ; background : var ( --surface ) ;
border-bottom : 1px solid var ( --border ) ; box-shadow : var ( --shadow ) ;
}
. icon-btn {
display : flex ; align-items : center ; justify-content : center ;
width : 2 . 2rem ; height : 2 . 2rem ; border-radius : 8px ; border : 1px solid transparent ;
background : transparent ; color : var ( --text ) ; cursor : pointer ;
}
. icon-btn : hover { background : var ( - - surface2 ) ; border-color : var ( - - border ) ; }
. brand {
display : flex ; align-items : center ; gap : . 5rem ; color : var ( --accent ) ;
font-weight : 700 ; text-decoration : none ; white-space : nowrap ;
}
. brand span { display : none ; }
@ media ( min-width : 900px ) { . brand span { display : inline ; } }
. site-search { position : relative ; flex : 1 ; max-width : 420 px ; margin : 0 auto ; }
. site-search input {
width : 100 % ; padding : . 5rem . 8rem . 5rem 2 . 2rem ; border-radius : 20px ;
border : 1px solid var ( --border ) ; background : var ( --surface2 ) ; color : var ( --text ) ;
font-size : . 92rem ;
}
. site-search input : focus { outline : 2 px solid var ( - - accent ) ; outline-offset : 1 px ; }
. such-icon { position : absolute ; left : .7 rem ; top : 50 % ; transform : translateY ( -50 % ) ; color : var ( - - muted ) ; }
. suche-ergebnisse {
position : absolute ; top : calc ( 100 % + . 4rem ) ; left : 0 ; right : 0 ; max-height : 60vh ;
overflow-y : auto ; background : var ( --surface ) ; border : 1px solid var ( --border ) ;
border-radius : var ( --radius ) ; box-shadow : var ( --shadow-lg ) ; z-index : 40 ;
}
. suche-ergebnisse a {
display : block ; padding : . 55rem . 9rem ; color : var ( --text ) ; text-decoration : none ;
border-bottom : 1px solid var ( --border ) ;
}
. suche-ergebnisse a : last-child { border-bottom : none ; }
. suche-ergebnisse a : hover , . suche-ergebnisse a . aktiv { background : var ( - - tint - indigo ) ; }
. suche-ergebnisse small { display : block ; color : var ( - - muted ) ; font-weight : 400 ; }
. suche-leer { padding : .8 rem .9 rem ; color : var ( - - muted ) ; }
# theme-toggle . icon-moon { display : none ; }
: root [ data-theme = "dark" ] # theme-toggle . icon-sun { display : none ; }
: root [ data-theme = "dark" ] # theme-toggle . icon-moon { display : inline ; }
@ media ( prefers-color-scheme : dark ) {
: root : not ( [ data-theme = "light" ] ) # theme-toggle . icon-sun { display : none ; }
: root : not ( [ data-theme = "light" ] ) # theme-toggle . icon-moon { display : inline ; }
}
/* --- Layout: Sidebar + Inhalt --- */
. site-body { display : flex ; align-items : flex-start ; max-width : 1400 px ; margin : 0 auto ; }
. sidebar {
position : sticky ; top : 3 . 6rem ; width : 290px ; flex : none ;
height : calc ( 100vh - 3 . 6rem ) ; overflow-y : auto ; border-right : 1px solid var ( --border ) ;
background : var ( --surface ) ; padding : 1rem 0 ;
}
. sidebar-gruppe { border-bottom : 1 px solid var ( - - border ) ; }
. sidebar-gruppe summary {
cursor : pointer ; padding : . 6rem 1rem ; font-weight : 700 ; color : var ( --accent ) ;
list-style : none ; display : flex ; align-items : center ; justify-content : space-between ;
}
. sidebar-gruppe summary :: -webkit-details-marker { display : none ; }
. sidebar-gruppe summary :: after { content : "▾" ; color : var ( - - muted ) ; font-size : .8 em ; }
. sidebar-gruppe [ open ] summary :: after { content : "▴" ; }
. sidebar ul { list-style : none ; margin : 0 ; padding : 0 .5 rem .5 rem ; }
. sidebar li a {
display : flex ; align-items : center ; gap : . 5rem ; padding : . 4rem . 6rem ;
border-radius : 8px ; color : var ( --text ) ; text-decoration : none ; font-size : . 92rem ;
}
. sidebar li a : hover { background : var ( - - tint - indigo ) ; }
. sidebar li . aktiv a { background : var ( - - accent ) ; color : var ( - - accent - text ) ; font-weight : 600 ; }
. fortschritt-haken { width : 1 em ; height : 1 em ; color : var ( - - emerald ) ; visibility : hidden ; }
. fortschritt-haken . icon { width : 100 % ; height : 100 % ; }
li [ data-erledigt = "1" ] . fortschritt-haken { visibility : visible ; }
. sidebar-extra { border-top : 2 px solid var ( - - border ) ; margin-top : .5 rem ; padding-top : .8 rem !important ; }
. sidebar-overlay { display : none ; }
. content { flex : 1 ; min-width : 0 ; padding : 1.5 rem 2 rem 5 rem ; max-width : 900 px ; margin : 0 auto ; }
/* --- Breadcrumb, Prev/Next --- */
. breadcrumb { font-size : .85 rem ; color : var ( - - muted ) ; margin-bottom : .8 rem ; }
. breadcrumb a { color : var ( - - muted ) ; }
. prev-next { display : flex ; justify-content : space-between ; gap : 1 rem ; margin : 1.2 rem 0 ; }
. prev-next-knopf {
display : flex ; align-items : center ; gap : . 6rem ; flex : 1 ; max-width : 48 % ;
padding : . 7rem 1rem ; border : 1px solid var ( --border ) ; border-radius : var ( --radius ) ;
background : var ( --surface ) ; color : var ( --text ) ; text-decoration : none ; font-size : . 9rem ;
box-shadow : var ( --shadow ) ; transition : border-color . 15s , transform . 15s ;
}
. prev-next-knopf : hover { border-color : var ( - - accent ) ; transform : translateY ( -1 px ) ; }
. prev-next-next { margin-left : auto ; text-align : right ; flex-direction : row-reverse ; }
. prev-next-knopf small { display : block ; color : var ( - - muted ) ; font-weight : 400 ; }
. prev-next-leer { flex : 1 ; max-width : 48 % ; }
. fortschritt-knopf {
display : inline-flex ; align-items : center ; gap : . 5rem ; margin : 1rem 0 ;
padding : . 55rem 1rem ; border-radius : 20px ; border : 1px solid var ( --border ) ;
background : var ( --surface2 ) ; color : var ( --text ) ; cursor : pointer ; font : inherit ; font-size : . 88rem ;
}
. fortschritt-knopf . icon { color : var ( - - muted ) ; }
. fortschritt-knopf . erledigt { background : var ( - - tint - emerald ) ; border-color : var ( - - emerald ) ; color : var ( - - emerald ) ; }
. fortschritt-knopf . erledigt . icon { color : var ( - - emerald ) ; }
. site-footer { text-align : center ; padding : 1.5 rem ; color : var ( - - muted ) ; font-size : .85 rem ; border-top : 1 px solid var ( - - border ) ; }
. site-footer a { color : var ( - - muted ) ; }
/* --- Typografie im Artikel (angelehnt an bilder/or_html_style.html) --- */
article h1 , article h2 , article h3 , article h4 , article h5 {
line-height : 1 . 25 ; font-weight : 700 ; margin-top : 2 . 1em ; margin-bottom : . 6em ; scroll-margin-top : 4 . 5rem ;
}
article h1 { color : var ( - - accent ) ; font-size : 1.9 rem ; border-bottom : 3 px solid var ( - - accent ) ; padding-bottom : .25 em ; }
article h2 { color : var ( - - accent ) ; font-size : 1.45 rem ; border-bottom : 2 px solid var ( - - border ) ; padding-bottom : .2 em ; }
article h3 { color : var ( - - cyan ) ; font-size : 1.18 rem ; }
article h4 { color : var ( - - violett ) ; font-size : 1.05 rem ; }
article h2 [ id $ = "-lernziele" ] :: before { content : "🎯 " ; }
article h2 [ id $ = "-uebungsaufgaben" ] :: before { content : "📝 " ; }
article h2 [ id $ = "-selbsttest" ] :: before { content : "✅ " ; }
article h2 [ id $ = "-zusammenfassung" ] :: before { content : "📌 " ; }
article a { color : var ( - - accent ) ; text-decoration : none ; }
article a : hover { text-decoration : underline ; }
article a [ target = "_blank" ] :: after { content : " \2197" ; font-size : .82 em ; color : var ( - - cyan ) ; text-decoration : none ; }
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.
2026-09-10 17:40:29 +02:00
/ * Glossar-Links : dezent ( gestrichelte Unterstreichung , nicht blau ) —
sie sollen auffindbar , aber nicht wie Navigationslinks aussehen . * /
article a . glossar-link {
color : inherit ;
text-decoration : underline dotted var ( --accent ) ;
text-decoration-thickness : 1px ;
text-underline-offset : 2px ;
}
article a . glossar-link : hover {
color : var ( --accent ) ;
text-decoration : underline solid var ( --accent ) ;
}
Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
article p { margin : .75 em 0 ; }
article hr { border : 0 ; border-top : 1 px solid var ( - - border ) ; margin : 2.2 em 0 ; }
article figure { margin : 2 em 0 ; text-align : center ; }
article img { max-width : 100 % ; height : auto ; display : block ; margin : 0 auto ; border-radius : 10 px ; }
article figure img { box-shadow : var ( - - shadow - lg ) ; }
article figcaption { color : var ( - - muted ) ; font-size : .9 rem ; font-style : italic ; margin-top : .6 em ; }
article table { border-collapse : collapse ; width : 100 % ; margin : 1.4 em 0 ; font-size : .94 rem ; box-shadow : var ( - - shadow ) ; border-radius : 8 px ; overflow : hidden ; }
article th { background : var ( - - accent ) ; color : var ( - - accent - text ) ; font-weight : 700 ; text-align : left ; }
article th , article td { padding : .55 em .75 em ; border : 1 px solid var ( - - border ) ; vertical-align : top ; }
article tbody tr : nth-child ( even ) { background : var ( - - tint - indigo ) ; }
article code { font-family : "DejaVu Sans Mono" , Consolas , Menlo , monospace ; font-size : .9 em ; background : var ( - - tint - violett ) ; color : var ( - - violett ) ; padding : .12 em .4 em ; border-radius : 5 px ; }
article pre { background : var ( - - code - bg ) ; color : var ( - - code - text ) ; padding : 1.1 em 1.2 em ; border-radius : 10 px ; overflow-x : auto ; line-height : 1.5 ; font-size : .85 rem ; box-shadow : var ( - - shadow - lg ) ; }
article pre code { background : none ; color : inherit ; padding : 0 ; font-size : inherit ; }
article div . sourceCode { position : relative ; }
article div . sourceCode pre . sourceCode { padding-top : 2.6 em ; }
article blockquote { border-left : 4 px solid var ( - - violett ) ; background : var ( - - tint - violett ) ; margin : 1.4 em 0 ; padding : .6 em 1.2 em ; border-radius : 0 8 px 8 px 0 ; }
article ul , article ol { padding-left : 1.4 em ; }
article li { margin : .3 em 0 ; }
. katex-display { overflow-x : auto ; overflow-y : hidden ; padding : .4 em 0 ; }
/* --- Code-Buttons (Kopieren / Herunterladen) --- */
. code-btn-leiste { position : absolute ; top : .55 em ; right : .55 em ; z-index : 2 ; display : flex ; gap : .4 em ; }
. code-btn , . download-link {
display : inline-flex ; align-items : center ; gap : . 35em ; background : rgba ( 255 , 255 , 255 , . 08 ) ;
color : # cbd5e1 ; border : 1px solid rgba ( 255 , 255 , 255 , . 18 ) ; border-radius : 6px ;
padding : . 3em . 65em ; font : inherit ; font-size : . 78rem ; cursor : pointer ;
text-decoration : none ! important ; transition : background . 15s , color . 15s ;
}
. code-btn : hover , . download-link : hover { background : rgba ( 255 , 255 , 255 , .18 ) ; color : #fff ; }
. code-btn . copied { background : var ( - - emerald ) ; color : #fff ; border-color : var ( - - emerald ) ; }
. download-link { position : static ; margin-left : .6 em ; }
/* --- Karten (Kapitel auf einen Blick, Lernpfade, Kapitelraster) --- */
. card { border-radius : var ( - - radius ) ; padding : 1.1 em 1.4 em ; margin : 1.6 em 0 ; box-shadow : var ( - - shadow ) ; }
. card p , . card blockquote { margin : .5 em 0 !important ; border : none !important ; background : none !important ; padding : 0 !important ; }
. card-blick { background : var ( - - tint - indigo ) ; border : 1 px solid var ( - - accent ) ; }
. card-blick blockquote { border-left : 3 px solid var ( - - accent ) ; padding-left : .8 em !important ; }
/ * --- Didaktische Karten der Version 04 ---------------------------------
Jede Kartenart traegt eine eigene Leitfarbe aus der Buchpalette , damit
Kursteilnehmer beim Blaettern sofort erkennen , was sie vor sich haben :
gruen = sofort ausprobieren , cyan = Formel im Klartext , violett = Excel ,
amber = Selbstkontrolle , rose = hier steckt ein Fehler drin . Die
Leitfarbe liegt links als 5px-Balken , damit sie auch im Ausdruck und in
der Druckvorschau erkennbar bleibt . * /
. card-schnellstart ,
. card-formel ,
. card-excel ,
. card-quiz ,
. card-denkfehler { border : 1 px solid var ( - - border ) ; border-left-width : 5 px ; }
. card-schnellstart { background : var ( - - tint - emerald ) ; border-left-color : var ( - - emerald ) ; }
. card-formel { background : var ( - - tint - cyan ) ; border-left-color : var ( - - cyan ) ; }
. card-excel { background : var ( - - tint - violett ) ; border-left-color : var ( - - violett ) ; }
. card-quiz { background : var ( - - tint - amber ) ; border-left-color : var ( - - amber ) ; }
. card-denkfehler { background : var ( - - tint - amber ) ; border-left-color : var ( - - rose ) ; }
/ * Die Karten enthalten haeufig Tabellen ( Formel-Uebersetzer ) und
Codebloecke ( Schnellstart , Denkfehler ) . Beide sollen sich klar vom
getoenten Kartengrund abheben statt darin zu verschwimmen . * /
. card-schnellstart pre ,
. card-denkfehler pre { margin : .8 em 0 !important ; }
. card-formel table { margin : .6 em 0 ; background : var ( - - surface ) ; }
. card-formel td : first-child { white-space : nowrap ; }
/ * --- Interaktive Plotly-Figuren ---------------------------------------
Stehen im Text unmittelbar hinter der statischen Fassung ( die im PDF
gedruckt wird ) . Auf schmalen Displays darf die Figur horizontal
scrollen , statt die Seite selbst breit zu machen . * /
. plotly-figur { margin : 1.4 em 0 ; background : var ( - - surface ) ; border : 1 px solid var ( - - border ) ;
border-radius : var ( --radius ) ; padding : . 6em ; overflow-x : auto ; }
. plotly-figur . plotly-graph-div { min-width : 320 px ; }
Folium-Karten: die Blockade war eine falsche Alternative
Der einzige Punkt, der in diesem Projekt je als "blockiert" gefuehrt wurde,
ist erledigt. Die Begruendung lautete: Die VRP-Instanz sei ein synthetisches
100 x 100-km-Raster ohne Geokoordinaten, eine echte Karte verlange also
ENTWEDER eine neue Instanz (und damit zwei Dutzend neue abgedruckte Zahlen)
ODER eine willkuerliche Verankerung.
Es gibt einen dritten Weg, und er ist exakt. Eine azimutal-aequidistante
Projektion um den Schwerpunkt des Rasters bildet die 17 Punkte so auf Laenge
und Breite ab, dass die Grosskreisdistanzen mit den Rasterdistanzen
uebereinstimmen:
festes cos(Breite) Median 0,360 % Maximum 2,003 %
cos der eigenen Breite Median 0,351 % Maximum 0,908 %
azimutal-aequidistant Median 0,000 % Maximum 0,001 %
Ein Meter auf hundert Kilometer. Kein abgedruckter Wert aendert sich, die
Gesamtstrecke bleibt bei 619 km, das SVG ist byte-identisch geblieben.
Zwei Befunde stuetzten die Entscheidung: "100 x 100 km Raster" steht nur in
zwei Codekommentaren, nirgends im Fliesstext; und der didaktische Punkt der
Bildunterschrift - die sich kreuzenden Touren, die KEIN Fehler sind - wird auf
einer Karte deutlicher.
erzeuge_vrp_touren.py schreibt jetzt SVG UND Karte aus einem Lauf. Ein zweites
Skript haette erneut geloest, und die Routing-Bibliothek arbeitet heuristisch.
pruefe_projektion() bricht ab, wenn die Verzerrung 0,01 % ueberschreitet - die
Zahl im Buch wird bei jedem Lauf bewiesen.
Neu im Build: die Marke {karte:name}, aufgeloest zu einem <iframe> (Leaflets
Stylesheet soll nicht in die Buchseite greifen), im PDF zum Verweissatz - wie
bei {plotly:}. --check bewacht jetzt beide Markenarten auf Existenz der Datei;
gegengeprueft mit einer absichtlich falschen Marke.
Zwei Vorkehrungen, die ohne Messung nicht aufgefallen waeren:
* Folium vergibt jedem Element eine uuid4 - jeder Lauf erzeugte eine andere
Datei. normalisiere() ersetzt sie durch fortlaufende Nummern; zwei Laeufe
liefern jetzt dieselbe Pruefsumme.
* Folium bindet ZEHN CDN-Ressourcen ein (jQuery, Bootstrap, Glyphicons,
FontAwesome, awesome-markers ...). Gebraucht werden ZWEI. leaflet.js und
leaflet.css liegen jetzt in web_04/assets/, die uebrigen acht entfallen.
Nachgeprueft: Die Karte benutzt ausschliesslich L.*-Aufrufe. Ohne Netz
fehlen nur die Kacheln, Marker und Touren bleiben sichtbar.
Gegenrechnung an vier Strecken: Depot -> K1 misst auf der Karte 27,20 km, die
Distanzmatrix sagt 27 - die Differenz stammt aus deren int(), nicht aus der
Geographie.
Veroeffentlicht: 8 Dateien uebertragen, alle dreizehn HTTP-Proben richtig, die
Karte auf dem Server unter 200 erreichbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 20:38:16 +02:00
/ * Leaflet-Karte im iframe ( { karte : ... } ) . Der Rahmen kommt von hier , nicht vom
iframe selbst - dessen Standardrahmen passt zu keinem der beiden Themen . * /
. karte-figur { display : block ; width : 100 % ; height : 30 rem ; margin : 1.4 em 0 ;
border : 1px solid var ( --border ) ; border-radius : var ( --radius ) ;
background : var ( --surface ) ; }
@ media ( max-width : 640px ) { . karte-figur { height : 22 rem ; } }
Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
/ * Der Formel-Uebersetzer ist zweispaltig gedacht : links die Mathematik ,
rechts der Klartext . Auf schmalen Displays wuerde die Formelspalte die
Tabelle sonst aus dem Viewport druecken . * /
@ media ( max-width : 640px ) {
. card-formel td : first-child { white-space : normal ; }
}
. kartenraster { display : grid ; grid-template-columns : repeat ( auto - fill , minmax ( 230 px , 1 fr ) ) ; gap : 1 rem ; margin : 1 rem 0 2 rem ; }
. karte { display : block ; background : var ( - - surface ) ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ; padding : 1.1 rem ; box-shadow : var ( - - shadow ) ; text-decoration : none ; color : var ( - - text ) ; }
. karte : hover { border-color : var ( - - accent ) ; transform : translateY ( -2 px ) ; transition : transform .15 s , border-color .15 s ; }
. karte h3 { margin : .3 em 0 ; color : var ( - - text ) ; font-size : 1.05 rem ; }
. karte p { color : var ( - - muted ) ; font-size : .88 rem ; margin : .4 em 0 ; }
. karte-nummer { font-size : .78 rem ; font-weight : 700 ; color : var ( - - accent ) ; text-transform : uppercase ; letter-spacing : .03 em ; }
. karte-zeit { font-size : .8 rem ; color : var ( - - muted ) ; }
. karte-pfad { background : var ( - - tint - violett ) ; border-color : var ( - - violett ) ; }
. karte-einstieg { background : var ( - - tint - cyan ) ; border-color : var ( - - cyan ) ; }
. kapitelgruppe { margin : 2.5 rem 0 ; }
. kapitelgruppe h2 { color : var ( - - accent ) ; border-bottom : 2 px solid var ( - - border ) ; padding-bottom : .3 em ; }
. lernpfade h2 { color : var ( - - violett ) ; }
/* --- Landing-Page Hero --- */
. hero { margin : 1 rem 0 2.5 rem ; }
. hero-kicker { color : var ( - - cyan ) ; letter-spacing : .12 em ; font-weight : 700 ; font-size : .85 rem ; }
. hero-text h1 { font-size : 2.1 rem ; color : var ( - - accent ) ; margin : .2 em 0 ; }
. hero-untertitel { color : var ( - - muted ) ; font-size : 1.05 rem ; }
. hero-badges { display : flex ; gap : .6 rem ; margin-top : 1 rem ; flex-wrap : wrap ; }
. hero-kompendium { color : var ( - - muted ) ; margin-top : 1.4 rem ; max-width : 640 px ; }
Titeltext: eine Quelle fuer PDF-Titelseite und Startseite
Die Startseite zeigte einen anderen Titeltext als das PDF. Der Text stand
VIERMAL im Repository: NEUER_TITEL.md, die Konstanten in
bilder_04/erzeuge_titelseite.py, der Titelblock des Vorworts - und noch einmal
fest einprogrammiert im Hero-Block von baue_landingpage().
Die Ursache ist messbar: Die drei Commits, die den Titel ueberarbeitet haben
(ca5621f, 199dd3b, 1659fb9 im Ursprungs-Repository), fassten jedes Mal
dieselben drei Dateien an. build_version_04.py war in keinem davon, und
OR_HTML_04/index.html taucht in ihren Dateilisten gar nicht auf. Online stand
deshalb:
Unterzeile "Von den mathematischen Grundlagen zur quantitativen
Portfolio-Optimierung an den Finanzmaerkten."
Kompendium "Ein vollstaendiges Kompendium mit Algorithmen ..."
Anwendungen fehlte ganz
Behandlung wie bei den neun Nummern-Funden zuvor: nicht die Kopie korrigieren,
sondern die Kopie abschaffen. Das neue titeltexte_04.py zerlegt NEUER_TITEL.md
in Titel, Untertitel, Kompendium- und Anwendungen-Absatz; PDF-Titelseite und
Startseite lesen dieselbe Funktion. Der Autor bleibt bewusst Konstante im
Generator - die Vorlage beschreibt das Buch, nicht sein Impressum.
Der entscheidende Beleg: Die neu erzeugte titelseite.svg ist BYTE-IDENTISCH
(97fb40d7...) zur vorherigen - der Umbau hat am PDF nachweislich nichts
veraendert. Auch nicht am Umbruch: Die frueher handgesetzten drei Zeilen
entstehen jetzt automatisch aus _umbrechen(), und zwar exakt so. Ein
Textvergleich ueber das SVG war uebrigens nicht moeglich - es enthaelt kein
einziges <text>-Element, matplotlib zeichnet alle 726 Buchstaben als Kurven.
Das Vorwort bleibt von Hand gepflegter Buchtext, wird aber von
pruefe_titeltexte() bei jedem --check gegen die Quelle gestellt. Gleich beim
ersten Lauf fand es einen echten Unterschied: Das Vorwort setzte einen
Geviertstrich, die Vorlage einen Halbgeviertstrich. Im Deutschen ist der
Halbgeviertstrich der richtige - das Vorwort folgt jetzt der Quelle.
Veroeffentlicht: 5 Dateien uebertragen, alle vierzehn HTTP-Proben richtig.
Gegenprobe vom Server: alle drei Absaetze der Startseite sind zeichengenau
identisch mit NEUER_TITEL.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:39:49 +02:00
. hero-anwendungen { color : var ( - - muted ) ; margin-top : .8 rem ; max-width : 640 px ; }
Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python"
(Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im
uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch
Version_03 (eingefroren) verwaltet und Version_04/ ignoriert.
Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit
der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen
Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist.
Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise,
325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren,
25 Notebooks, PDF mit 715 Seiten.
Zusaetzlich in diesem Commit:
* pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api,
figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt
unveraendert daneben bestehen. ortools steht in der Grundausstattung,
highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der
schlanken Installation gar nicht erst auftreten.
* Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py
importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo
steht in requirements.txt, wird aber von keinem Programm importiert,
sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in
[empfehlungen]).
* NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es
ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden
Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen.
* PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den
tatsaechlichen aus erzeuge_titelseite.py korrigiert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 01:20:09 +02:00
. hero-autor { color : var ( - - muted ) ; font-size : .92 rem ; line-height : 1.5 ; }
. badge { padding : .35 em .9 em ; border-radius : 20 px ; color : #fff ; font-weight : 600 ; font-size : .85 rem ; }
. badge-indigo { background : var ( - - accent ) ; }
. badge-cyan { background : var ( - - cyan ) ; }
. badge-violett { background : var ( - - violett ) ; }
. badge-emerald { background : var ( - - emerald ) ; }
/* --- Programme-Seite --- */
. programmgruppe { margin : 2 rem 0 ; }
. programmliste { list-style : none ; padding : 0 ; }
. programmliste li { display : flex ; align-items : center ; justify-content : space-between ; padding : .5 em .8 em ; border : 1 px solid var ( - - border ) ; border-radius : 8 px ; margin : .4 em 0 ; background : var ( - - surface ) ; }
/* --- Stichwortverzeichnis --- */
. register-sprungleiste { display : flex ; flex-wrap : wrap ; gap : .3 rem ; margin : 1.2 rem 0 1.8 rem ; position : sticky ; top : 3.6 rem ; background : var ( - - bg ) ; padding : .5 rem 0 ; z-index : 5 ; }
. register-sprungleiste a { display : inline-flex ; align-items : center ; justify-content : center ; width : 1.8 rem ; height : 1.8 rem ; border-radius : 6 px ; background : var ( - - surface2 ) ; color : var ( - - accent ) ; text-decoration : none ; font-weight : 700 ; font-size : .85 rem ; }
. register-sprungleiste a : hover { background : var ( - - tint - indigo ) ; }
. register-buchstabe { color : var ( - - accent ) ; border-bottom : 2 px solid var ( - - border ) ; padding-bottom : .15 em ; margin-top : 2 em ; font-size : 1.3 rem ; }
. register-eintrag { padding : .55 em 0 ; border-bottom : 1 px solid var ( - - border ) ; }
. register-begriff { font-weight : 700 ; }
. register-eintrag a { font-size : .92 rem ; }
. register-unter { list-style : none ; margin : .4 em 0 0 ; padding-left : 1.4 em ; }
. register-unter li { margin : .3 em 0 ; color : var ( - - muted ) ; font-size : .92 rem ; }
. register-unter a { font-size : inherit ; }
/* --- Mobile --- */
@ media ( max-width : 899px ) {
. sidebar {
position : fixed ; top : 0 ; left : 0 ; height : 100vh ; z-index : 50 ; transform : translateX ( -105 % ) ;
transition : transform . 2s ease ; box-shadow : var ( --shadow-lg ) ; padding-top : 3 . 6rem ;
}
. sidebar . offen { transform : translateX ( 0 ) ; }
. sidebar-overlay . offen { display : block ; position : fixed ; inset : 0 ; background : rgba ( 0 , 0 , 0 , .4 ) ; z-index : 40 ; }
. content { padding : 1.2 rem 1 rem 4 rem ; }
. site-search { max-width : none ; }
}