kleine Änderungen

This commit is contained in:
dschlueter 2026-09-09 07:37:07 +02:00
commit dc088fa214
47 changed files with 183868 additions and 573 deletions

View file

@ -170,19 +170,27 @@ gehört wie die Arbeit, die sie beschreibt.
## Diagramme
`bilder_04/erzeuge_*.py` erzeugen 29 der 42 Kapiteldiagramme, jeweils **aus derselben
Instanz wie das zugehörige Buchprogramm**. Die elf neueren benutzen `bilder_04/stil_04.py`
— Palette, `rcParams` und `speichere()` an einer Stelle statt elfmal; die älteren setzen
`svg.hashsalt` und `metadata={"Date": None}` weiterhin selbst. (Das 33. SVG im Verzeichnis ist `titelseite.svg`,
die der Build bei jedem Lauf selbst schreibt — deshalb nennt `README.md` 33 und diese Datei
32.) Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten
33 Generatoren `bilder_04/erzeuge_*.py` erzeugen **38 der 48 Kapiteldiagramme**, jeweils **aus
derselben Instanz wie das zugehörige Buchprogramm**. 17 davon benutzen `bilder_04/stil_04.py`
— Palette, `rcParams` und `speichere()` an einer Stelle statt siebzehnmal; die älteren setzen
`svg.hashsalt` und `metadata={"Date": None}` weiterhin selbst. (Das 49. SVG im Verzeichnis ist
`titelseite.svg`, die der Build bei jedem Lauf selbst schreibt und die im Buch nicht als
Abbildung erscheint.) Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten
sieben einen Fehler zutage — dreimal ein Modell, das im Buch gar nicht vorkommt, einmal
widersprüchliche Zahlen zwischen Bild und Text, einmal ein gekipptes Vorzeichen. Wer ein
Diagramm anfasst, vergleicht es zuerst mit dem Modell des Kapitels.
Die übrigen 14 sind schematisch (Kästen, Pfeile, beschriftete Formeln) und bekommen bewusst
Die übrigen 10 sind schematisch (Kästen, Pfeile, beschriftete Formeln) und bekommen bewusst
keinen Generator — dort kann nichts driften. Das Kriterium steht in `PROGRESS.md`
Abschnitt 6c.
Abschnitt 6c. Es gilt aber nur, solange ein Bild keine Zahl und keine Aufzählung aus dem Text
wiederholt: `kap15_or_plattform.svg` war handgezeichnet und zeigte **fünf** Schichten neben
einer Tabelle mit **sechs** — seither wird es aus eben dieser Tabelle erzeugt.
**Ein neues Bild ergänzt in der Regel, es ersetzt nicht.** Bei den neun Überarbeitungen der
Stufe 2 war nur eines wirklich ein Ersatz (die OR-Architektur); in den übrigen Fällen zeigte
das vorhandene Diagramm etwas anderes — die Sehnenbedingung neben der Talkarte, den Suchbaum
neben der Polyedergeometrie, die Verteilung neben der Subadditivität. Vor dem Überschreiben
eines SVG also prüfen, was es zeigt, statt nur, wo es steht.
Konventionen der Generatoren: `plt.rcParams["svg.hashsalt"] = "or-mit-python-v04"` und
`metadata={"Date": None}` für byteidentische Läufe — **beides, sonst trägt das SVG einen

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 1.7 MiB

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="648pt" height="230.4pt" viewBox="0 0 648 230.4" xmlns="http://www.w3.org/2000/svg" version="1.1">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="648pt" height="244.8pt" viewBox="0 0 648 244.8" xmlns="http://www.w3.org/2000/svg" version="1.1">
<metadata>
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<cc:Work>
@ -20,8 +20,8 @@
</defs>
<g id="figure_1">
<g id="patch_1">
<path d="M 0 230.4
L 648 230.4
<path d="M 0 244.8
L 648 244.8
L 648 0
L 0 0
z
@ -29,83 +29,83 @@ z
</g>
<g id="axes_1">
<g id="patch_2">
<path d="M 72.79 188.44
L 630.81 188.44
<path d="M 72.79 202.84
L 630.81 202.84
L 630.81 25.44
L 72.79 25.44
z
" style="fill: #ffffff"/>
</g>
<g id="patch_3">
<path d="M 174.248182 32.849091
L 326.435455 32.849091
L 326.435455 64.809875
L 174.248182 64.809875
<path d="M 174.248182 33.503636
L 326.435455 33.503636
L 326.435455 68.28795
L 174.248182 68.28795
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_4">
<path d="M 326.435455 90.959608
L 427.893636 90.959608
L 427.893636 122.920392
L 326.435455 122.920392
<path d="M 326.435455 96.747843
L 427.893636 96.747843
L 427.893636 131.532157
L 326.435455 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_5">
<path d="M 427.893636 149.070125
L 529.351818 149.070125
L 529.351818 181.030909
L 427.893636 181.030909
<path d="M 427.893636 159.99205
L 529.351818 159.99205
L 529.351818 194.776364
L 427.893636 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_6">
<path d="M 72.79 32.849091
L 174.248182 32.849091
L 174.248182 64.809875
L 72.79 64.809875
<path d="M 72.79 33.503636
L 174.248182 33.503636
L 174.248182 68.28795
L 72.79 68.28795
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_7">
<path d="M 174.248182 149.070125
L 224.977273 149.070125
L 224.977273 181.030909
L 174.248182 181.030909
<path d="M 174.248182 159.99205
L 224.977273 159.99205
L 224.977273 194.776364
L 174.248182 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_8">
<path d="M 427.893636 90.959608
L 630.81 90.959608
L 630.81 122.920392
L 427.893636 122.920392
<path d="M 427.893636 96.747843
L 630.81 96.747843
L 630.81 131.532157
L 427.893636 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_9">
<path d="M 72.79 90.959608
L 275.706364 90.959608
L 275.706364 122.920392
L 72.79 122.920392
<path d="M 72.79 96.747843
L 275.706364 96.747843
L 275.706364 131.532157
L 72.79 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_10">
<path d="M 275.706364 149.070125
L 427.893636 149.070125
L 427.893636 181.030909
L 275.706364 181.030909
<path d="M 275.706364 159.99205
L 427.893636 159.99205
L 427.893636 194.776364
L 275.706364 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="matplotlib.axis_1">
<g id="xtick_1">
<g id="line2d_1">
<path d="M 72.79 188.44
<path d="M 72.79 202.84
L 72.79 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_2">
<defs>
@ -114,12 +114,12 @@ L 0 3.5
" style="stroke: #000000; stroke-width: 0.8"/>
</defs>
<g>
<use xlink:href="#mf0a04b41f8" x="72.79" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="72.79" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_1">
<!-- 0 -->
<g transform="translate(69.60875 203.038437) scale(0.1 -0.1)">
<g transform="translate(69.60875 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-30" d="M 2034 4250
Q 1547 4250 1301 3770
@ -149,18 +149,18 @@ z
</g>
<g id="xtick_2">
<g id="line2d_3">
<path d="M 123.519091 188.44
<path d="M 123.519091 202.84
L 123.519091 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_4">
<g>
<use xlink:href="#mf0a04b41f8" x="123.519091" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="123.519091" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_2">
<!-- 1 -->
<g transform="translate(120.337841 203.038437) scale(0.1 -0.1)">
<g transform="translate(120.337841 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-31" d="M 794 531
L 1825 531
@ -183,18 +183,18 @@ z
</g>
<g id="xtick_3">
<g id="line2d_5">
<path d="M 174.248182 188.44
<path d="M 174.248182 202.84
L 174.248182 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_6">
<g>
<use xlink:href="#mf0a04b41f8" x="174.248182" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="174.248182" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_3">
<!-- 2 -->
<g transform="translate(171.066932 203.038437) scale(0.1 -0.1)">
<g transform="translate(171.066932 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-32" d="M 1228 531
L 3431 531
@ -227,18 +227,18 @@ z
</g>
<g id="xtick_4">
<g id="line2d_7">
<path d="M 224.977273 188.44
<path d="M 224.977273 202.84
L 224.977273 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_8">
<g>
<use xlink:href="#mf0a04b41f8" x="224.977273" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="224.977273" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_4">
<!-- 3 -->
<g transform="translate(221.796023 203.038437) scale(0.1 -0.1)">
<g transform="translate(221.796023 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-33" d="M 2597 2516
Q 3050 2419 3304 2112
@ -279,18 +279,18 @@ z
</g>
<g id="xtick_5">
<g id="line2d_9">
<path d="M 275.706364 188.44
<path d="M 275.706364 202.84
L 275.706364 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_10">
<g>
<use xlink:href="#mf0a04b41f8" x="275.706364" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="275.706364" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_5">
<!-- 4 -->
<g transform="translate(272.525114 203.038437) scale(0.1 -0.1)">
<g transform="translate(272.525114 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-34" d="M 2419 4116
L 825 1625
@ -318,18 +318,18 @@ z
</g>
<g id="xtick_6">
<g id="line2d_11">
<path d="M 326.435455 188.44
<path d="M 326.435455 202.84
L 326.435455 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_12">
<g>
<use xlink:href="#mf0a04b41f8" x="326.435455" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="326.435455" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_6">
<!-- 5 -->
<g transform="translate(323.254205 203.038437) scale(0.1 -0.1)">
<g transform="translate(323.254205 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-35" d="M 691 4666
L 3169 4666
@ -363,18 +363,18 @@ z
</g>
<g id="xtick_7">
<g id="line2d_13">
<path d="M 377.164545 188.44
<path d="M 377.164545 202.84
L 377.164545 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_14">
<g>
<use xlink:href="#mf0a04b41f8" x="377.164545" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="377.164545" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_7">
<!-- 6 -->
<g transform="translate(373.983295 203.038437) scale(0.1 -0.1)">
<g transform="translate(373.983295 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-36" d="M 2113 2584
Q 1688 2584 1439 2293
@ -413,18 +413,18 @@ z
</g>
<g id="xtick_8">
<g id="line2d_15">
<path d="M 427.893636 188.44
<path d="M 427.893636 202.84
L 427.893636 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_16">
<g>
<use xlink:href="#mf0a04b41f8" x="427.893636" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="427.893636" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_8">
<!-- 7 -->
<g transform="translate(424.712386 203.038437) scale(0.1 -0.1)">
<g transform="translate(424.712386 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-37" d="M 525 4666
L 3525 4666
@ -443,18 +443,18 @@ z
</g>
<g id="xtick_9">
<g id="line2d_17">
<path d="M 478.622727 188.44
<path d="M 478.622727 202.84
L 478.622727 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_18">
<g>
<use xlink:href="#mf0a04b41f8" x="478.622727" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="478.622727" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_9">
<!-- 8 -->
<g transform="translate(475.441477 203.038437) scale(0.1 -0.1)">
<g transform="translate(475.441477 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-38" d="M 2034 2216
Q 1584 2216 1326 1975
@ -502,18 +502,18 @@ z
</g>
<g id="xtick_10">
<g id="line2d_19">
<path d="M 529.351818 188.44
<path d="M 529.351818 202.84
L 529.351818 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_20">
<g>
<use xlink:href="#mf0a04b41f8" x="529.351818" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="529.351818" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_10">
<!-- 9 -->
<g transform="translate(526.170568 203.038437) scale(0.1 -0.1)">
<g transform="translate(526.170568 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-39" d="M 703 97
L 703 672
@ -552,18 +552,18 @@ z
</g>
<g id="xtick_11">
<g id="line2d_21">
<path d="M 580.080909 188.44
<path d="M 580.080909 202.84
L 580.080909 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_22">
<g>
<use xlink:href="#mf0a04b41f8" x="580.080909" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="580.080909" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_11">
<!-- 10 -->
<g transform="translate(573.718409 203.038437) scale(0.1 -0.1)">
<g transform="translate(573.718409 217.438437) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
</g>
@ -571,18 +571,18 @@ L 580.080909 25.44
</g>
<g id="xtick_12">
<g id="line2d_23">
<path d="M 630.81 188.44
<path d="M 630.81 202.84
L 630.81 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_24">
<g>
<use xlink:href="#mf0a04b41f8" x="630.81" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="630.81" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_12">
<!-- 11 -->
<g transform="translate(624.4475 203.038437) scale(0.1 -0.1)">
<g transform="translate(624.4475 217.438437) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use xlink:href="#DejaVuSans-31" transform="translate(63.623047 0)"/>
</g>
@ -590,7 +590,7 @@ L 630.81 25.44
</g>
<g id="text_13">
<!-- Zeiteinheiten -->
<g transform="translate(318.475 216.716562) scale(0.1 -0.1)">
<g transform="translate(318.475 231.116562) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-5a" d="M 359 4666
L 4025 4666
@ -728,12 +728,12 @@ L -3.5 0
" style="stroke: #000000; stroke-width: 0.8"/>
</defs>
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="48.829483" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="50.895793" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_14">
<!-- Fraese -->
<g transform="translate(33.00875 52.628702) scale(0.1 -0.1)">
<g transform="translate(33.00875 54.695012) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-46" d="M 628 4666
L 3309 4666
@ -842,12 +842,12 @@ z
<g id="ytick_2">
<g id="line2d_26">
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="106.94" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="114.14" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_15">
<!-- Dreherei -->
<g transform="translate(22.743125 110.739219) scale(0.1 -0.1)">
<g transform="translate(22.743125 117.939219) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-44" d="M 1259 4147
L 1259 519
@ -883,12 +883,12 @@ z
<g id="ytick_3">
<g id="line2d_27">
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="165.050517" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="177.384207" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_16">
<!-- Lackiererei -->
<g transform="translate(11.013438 168.849736) scale(0.1 -0.1)">
<g transform="translate(11.013438 181.183426) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-4c" d="M 628 4666
L 1259 4666
@ -951,13 +951,29 @@ z
</g>
</g>
<g id="patch_11">
<path d="M 72.79 188.44
L 630.81 188.44
<path d="M 275.706364 96.747843
L 326.435455 96.747843
L 326.435455 131.532157
L 275.706364 131.532157
z
" clip-path="url(#p3043647a47)" style="fill: url(#h8ac0198506); stroke: #be123c; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_12">
<path d="M 224.977273 159.99205
L 275.706364 159.99205
L 275.706364 194.776364
L 224.977273 194.776364
z
" clip-path="url(#p3043647a47)" style="fill: url(#h8ac0198506); stroke: #be123c; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_13">
<path d="M 72.79 202.84
L 630.81 202.84
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
</g>
<g id="text_17">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(242.993381 51.588858) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(242.993381 53.655168) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-41" d="M 3419 850
L 1538 850
@ -1003,21 +1019,21 @@ z
</g>
<g id="text_18">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(369.816108 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(369.816108 116.899375) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-30" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_19">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(471.27429 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(471.27429 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-30" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_20">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(116.170653 51.588858) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(116.170653 53.655168) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-31" d="M 750 831
L 1813 831
@ -1040,21 +1056,21 @@ z
</g>
<g id="text_21">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(192.26429 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(192.26429 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-31" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_22">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(522.003381 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(522.003381 116.899375) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-31" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_23">
<!-- A2 -->
<g style="fill: #ffffff" transform="translate(166.899744 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(166.899744 116.899375) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-32" d="M 1844 884
L 3897 884
@ -1085,14 +1101,14 @@ z
</g>
<g id="text_24">
<!-- A2 -->
<g style="fill: #ffffff" transform="translate(344.451563 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(344.451563 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-32" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_25">
<!-- Job-Shop-Plan, Makespan = 11 -->
<g transform="translate(267.519375 19.44) scale(0.11 -0.11)">
<!-- Job-Shop-Plan: Makespan 11, untere Schranke 10 — schraffiert steht die Maschine still -->
<g transform="translate(114.118359 19.44) scale(0.11 -0.11)">
<defs>
<path id="DejaVuSans-4a" d="M 628 4666
L 1259 4666
@ -1246,14 +1262,18 @@ L 603 0
L 603 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2c" d="M 750 794
<path id="DejaVuSans-3a" d="M 750 794
L 1409 794
L 1409 256
L 897 -744
L 494 -744
L 750 256
L 1409 0
L 750 0
L 750 794
z
M 750 3309
L 1409 3309
L 1409 2516
L 750 2516
L 750 3309
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-20" transform="scale(0.015625)"/>
<path id="DejaVuSans-4d" d="M 628 4666
@ -1272,17 +1292,89 @@ L 628 0
L 628 4666
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-3d" d="M 678 2906
L 4684 2906
L 4684 2381
L 678 2381
L 678 2906
<path id="DejaVuSans-2c" d="M 750 794
L 1409 794
L 1409 256
L 897 -744
L 494 -744
L 750 256
L 750 794
z
M 678 1631
L 4684 1631
L 4684 1100
L 678 1100
L 678 1631
" transform="scale(0.015625)"/>
<path id="DejaVuSans-75" d="M 544 1381
L 544 3500
L 1119 3500
L 1119 1403
Q 1119 906 1312 657
Q 1506 409 1894 409
Q 2359 409 2629 706
Q 2900 1003 2900 1516
L 2900 3500
L 3475 3500
L 3475 0
L 2900 0
L 2900 538
Q 2691 219 2414 64
Q 2138 -91 1772 -91
Q 1169 -91 856 284
Q 544 659 544 1381
z
M 1991 3584
L 1991 3584
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2014" d="M 313 1978
L 6088 1978
L 6088 1528
L 313 1528
L 313 1978
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-66" d="M 2375 4863
L 2375 4384
L 1825 4384
Q 1516 4384 1395 4259
Q 1275 4134 1275 3809
L 1275 3500
L 2222 3500
L 2222 3053
L 1275 3053
L 1275 0
L 697 0
L 697 3053
L 147 3053
L 147 3500
L 697 3500
L 697 3744
Q 697 4328 969 4595
Q 1241 4863 1831 4863
L 2375 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-64" d="M 2906 2969
L 2906 4863
L 3481 4863
L 3481 0
L 2906 0
L 2906 525
Q 2725 213 2448 61
Q 2172 -91 1784 -91
Q 1150 -91 751 415
Q 353 922 353 1747
Q 353 2572 751 3078
Q 1150 3584 1784 3584
Q 2172 3584 2448 3432
Q 2725 3281 2906 2969
z
M 947 1747
Q 947 1113 1208 752
Q 1469 391 1925 391
Q 2381 391 2643 752
Q 2906 1113 2906 1747
Q 2906 2381 2643 2742
Q 2381 3103 1925 3103
Q 1469 3103 1208 2742
Q 947 2381 947 1747
z
" transform="scale(0.015625)"/>
</defs>
@ -1299,28 +1391,157 @@ z
<use xlink:href="#DejaVuSans-6c" transform="translate(538.134766 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(565.917969 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(627.197266 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(690.576172 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(722.363281 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(754.150391 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(840.429688 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(901.708984 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(955.994141 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(1017.517578 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(1069.617188 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(1133.09375 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1194.373047 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1257.751953 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(1289.539062 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1373.328125 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1405.115234 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1468.738281 0)"/>
<use xlink:href="#DejaVuSans-3a" transform="translate(690.576172 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(724.267578 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(756.054688 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(842.333984 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(903.613281 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(957.898438 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(1019.421875 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(1071.521484 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(1134.998047 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1196.277344 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1259.65625 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1291.443359 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1355.066406 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(1418.689453 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1450.476562 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1482.263672 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1545.642578 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1609.021484 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1648.230469 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(1709.753906 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1748.617188 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1810.140625 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(1841.927734 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(1905.404297 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1960.384766 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2023.763672 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(2064.876953 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(2126.15625 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(2189.535156 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2243.820312 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2305.34375 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(2337.130859 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(2400.753906 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2464.376953 0)"/>
<use xlink:href="#DejaVuSans-2014" transform="translate(2496.164062 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2596.164062 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(2627.951172 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(2680.050781 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(2735.03125 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2798.410156 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(2839.523438 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(2900.802734 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(2936.007812 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(2971.212891 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2998.996094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(3060.519531 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3101.632812 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3140.841797 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(3172.628906 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3224.728516 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(3263.9375 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(3325.460938 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3388.839844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3428.048828 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(3459.835938 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(3523.3125 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(3551.095703 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3612.619141 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(3644.40625 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(3730.685547 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(3791.964844 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(3844.064453 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(3899.044922 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(3962.423828 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(3990.207031 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(4053.585938 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(4115.109375 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(4146.896484 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(4198.996094 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(4238.205078 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(4265.988281 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(4293.771484 0)"/>
</g>
</g>
<g id="line2d_28">
<path d="M 580.080909 202.84
L 580.080909 25.44
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #059669; stroke-width: 1.5"/>
</g>
<g id="text_26">
<!-- untere Schranke 10 -->
<g style="fill: #059669" transform="translate(490.514121 195.750266) scale(0.085 -0.085)">
<use xlink:href="#DejaVuSans-75"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(63.378906 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(126.757812 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(165.966797 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(227.490234 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(266.353516 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(327.876953 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(359.664062 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(423.140625 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(478.121094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(541.5 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(582.613281 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(643.892578 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(707.271484 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(761.556641 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(823.080078 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(854.867188 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(918.490234 0)"/>
</g>
</g>
</g>
</g>
<defs>
<clipPath id="pce73ba693e">
<rect x="72.79" y="25.44" width="558.02" height="163"/>
<clipPath id="p3043647a47">
<rect x="72.79" y="25.44" width="558.02" height="177.4"/>
</clipPath>
</defs>
<defs>
<pattern id="h8ac0198506" patternUnits="userSpaceOnUse" x="0" y="0" width="72" height="72">
<rect x="0" y="0" width="73" height="73" fill="none"/>
<path d="M -36 36
L 36 -36
M -32 40
L 40 -32
M -28 44
L 44 -28
M -24 48
L 48 -24
M -20 52
L 52 -20
M -16 56
L 56 -16
M -12 60
L 60 -12
M -8 64
L 64 -8
M -4 68
L 68 -4
M 0 72
L 72 0
M 4 76
L 76 4
M 8 80
L 80 8
M 12 84
L 84 12
M 16 88
L 88 16
M 20 92
L 92 20
M 24 96
L 96 24
M 28 100
L 100 28
M 32 104
L 104 32
M 36 108
L 108 36
" style="fill: #be123c; stroke: #be123c; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/>
</pattern>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 103 KiB

View file

@ -749,6 +749,260 @@ L 492.615285 242.66828
<path d="M 380.229919 330.28
L 380.229919 37.968
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 4.81,2.08; stroke-dashoffset: 0; stroke: #b45309; stroke-opacity: 0.8; stroke-width: 1.3"/>
</g>
<g id="line2d_29">
<path d="M 46.82 294.907792
L 51.474343 296.738929
L 56.128687 298.570066
L 60.78303 300.401203
L 65.437374 302.23234
L 70.091717 304.063477
L 74.746061 305.894614
L 79.400404 307.725751
L 84.054747 309.556888
L 88.709091 311.388025
L 93.363434 313.219162
L 98.017778 315.050299
L 102.672121 316.881436
L 107.326465 318.712573
L 111.980808 320.54371
L 116.635152 322.374847
L 121.289495 324.205984
L 125.943838 326.037121
L 130.598182 327.868259
L 135.252525 329.699396
L 139.906869 331.530533
L 144.561212 333.36167
L 149.215556 335.192807
L 153.869899 337.023944
L 158.524242 338.855081
L 163.178586 340.686218
L 167.832929 342.517355
L 172.487273 344.348492
L 177.141616 346.179629
L 181.79596 348.010766
L 186.450303 349.841903
L 191.104646 351.67304
L 195.75899 353.504177
L 200.413333 355.335314
L 205.067677 357.166451
L 209.72202 358.997588
L 214.376364 360.828725
L 219.030707 362.659862
L 223.685051 364.491
L 228.339394 366.322137
L 232.993737 368.153274
L 237.648081 369.984411
L 242.302424 371.815548
L 246.956768 373.646685
L 251.413305 375.4
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="line2d_30">
<path d="M 46.82 98.395523
L 51.474343 100.22666
L 56.128687 102.057797
L 60.78303 103.888934
L 65.437374 105.720071
L 70.091717 107.551208
L 74.746061 109.382345
L 79.400404 111.213482
L 84.054747 113.044619
L 88.709091 114.875756
L 93.363434 116.706893
L 98.017778 118.53803
L 102.672121 120.369167
L 107.326465 122.200304
L 111.980808 124.031441
L 116.635152 125.862578
L 121.289495 127.693716
L 125.943838 129.524853
L 130.598182 131.35599
L 135.252525 133.187127
L 139.906869 135.018264
L 144.561212 136.849401
L 149.215556 138.680538
L 153.869899 140.511675
L 158.524242 142.342812
L 163.178586 144.173949
L 167.832929 146.005086
L 172.487273 147.836223
L 177.141616 149.66736
L 181.79596 151.498497
L 186.450303 153.329634
L 191.104646 155.160771
L 195.75899 156.991908
L 200.413333 158.823045
L 205.067677 160.654182
L 209.72202 162.485319
L 214.376364 164.316457
L 219.030707 166.147594
L 223.685051 167.978731
L 228.339394 169.809868
L 232.993737 171.641005
L 237.648081 173.472142
L 242.302424 175.303279
L 246.956768 177.134416
L 251.611111 178.965553
L 256.265455 180.79669
L 260.919798 182.627827
L 265.574141 184.458964
L 270.228485 186.290101
L 274.882828 188.121238
L 279.537172 189.952375
L 284.191515 191.783512
L 288.845859 193.614649
L 293.500202 195.445786
L 298.154545 197.276923
L 302.808889 199.108061
L 307.463232 200.939198
L 312.117576 202.770335
L 316.771919 204.601472
L 321.426263 206.432609
L 326.080606 208.263746
L 330.734949 210.094883
L 335.389293 211.92602
L 340.043636 213.757157
L 344.69798 215.588294
L 349.352323 217.419431
L 354.006667 219.250568
L 358.66101 221.081705
L 363.315354 222.912842
L 367.969697 224.743979
L 372.62404 226.575116
L 377.278384 228.406253
L 381.932727 230.23739
L 386.587071 232.068527
L 391.241414 233.899664
L 395.895758 235.730802
L 400.550101 237.561939
L 405.204444 239.393076
L 409.858788 241.224213
L 414.513131 243.05535
L 419.167475 244.886487
L 423.821818 246.717624
L 428.476162 248.548761
L 433.130505 250.379898
L 437.784848 252.211035
L 442.439192 254.042172
L 447.093535 255.873309
L 451.747879 257.704446
L 456.402222 259.535583
L 461.056566 261.36672
L 465.710909 263.197857
L 470.365253 265.028994
L 475.019596 266.860131
L 479.673939 268.691268
L 484.328283 270.522406
L 488.982626 272.353543
L 493.63697 274.18468
L 498.291313 276.015817
L 502.945657 277.846954
L 507.6 279.678091
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="line2d_31">
<path d="M 46.82 61.549472
L 51.474343 63.380609
L 56.128687 65.211746
L 60.78303 67.042883
L 65.437374 68.87402
L 70.091717 70.705158
L 74.746061 72.536295
L 79.400404 74.367432
L 84.054747 76.198569
L 88.709091 78.029706
L 93.363434 79.860843
L 98.017778 81.69198
L 102.672121 83.523117
L 107.326465 85.354254
L 111.980808 87.185391
L 116.635152 89.016528
L 121.289495 90.847665
L 125.943838 92.678802
L 130.598182 94.509939
L 135.252525 96.341076
L 139.906869 98.172213
L 144.561212 100.00335
L 149.215556 101.834487
L 153.869899 103.665624
L 158.524242 105.496761
L 163.178586 107.327899
L 167.832929 109.159036
L 172.487273 110.990173
L 177.141616 112.82131
L 181.79596 114.652447
L 186.450303 116.483584
L 191.104646 118.314721
L 195.75899 120.145858
L 200.413333 121.976995
L 205.067677 123.808132
L 209.72202 125.639269
L 214.376364 127.470406
L 219.030707 129.301543
L 223.685051 131.13268
L 228.339394 132.963817
L 232.993737 134.794954
L 237.648081 136.626091
L 242.302424 138.457228
L 246.956768 140.288365
L 251.611111 142.119503
L 256.265455 143.95064
L 260.919798 145.781777
L 265.574141 147.612914
L 270.228485 149.444051
L 274.882828 151.275188
L 279.537172 153.106325
L 284.191515 154.937462
L 288.845859 156.768599
L 293.500202 158.599736
L 298.154545 160.430873
L 302.808889 162.26201
L 307.463232 164.093147
L 312.117576 165.924284
L 316.771919 167.755421
L 321.426263 169.586558
L 326.080606 171.417695
L 330.734949 173.248832
L 335.389293 175.079969
L 340.043636 176.911106
L 344.69798 178.742244
L 349.352323 180.573381
L 354.006667 182.404518
L 358.66101 184.235655
L 363.315354 186.066792
L 367.969697 187.897929
L 372.62404 189.729066
L 377.278384 191.560203
L 381.932727 193.39134
L 386.587071 195.222477
L 391.241414 197.053614
L 395.895758 198.884751
L 400.550101 200.715888
L 405.204444 202.547025
L 409.858788 204.378162
L 414.513131 206.209299
L 419.167475 208.040436
L 423.821818 209.871573
L 428.476162 211.70271
L 433.130505 213.533848
L 437.784848 215.364985
L 442.439192 217.196122
L 447.093535 219.027259
L 451.747879 220.858396
L 456.402222 222.689533
L 461.056566 224.52067
L 465.710909 226.351807
L 470.365253 228.182944
L 475.019596 230.014081
L 479.673939 231.845218
L 484.328283 233.676355
L 488.982626 235.507492
L 493.63697 237.338629
L 498.291313 239.169766
L 502.945657 241.000903
L 507.6 242.83204
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="patch_4">
<path d="M 46.82 330.28
@ -1728,7 +1982,7 @@ z
<use xlink:href="#DejaVuSans-6c" transform="translate(1244.861328 0)"/>
</g>
</g>
<g id="line2d_29">
<g id="line2d_32">
<path d="M 80.53561 308.17237
L 80.53561 111.660101
L 361.499024 185.352202
@ -1776,7 +2030,7 @@ z
</g>
</g>
<g id="legend_1">
<g id="line2d_30">
<g id="line2d_33">
<path d="M 394.72 48.44675
L 402.72 48.44675
L 410.72 48.44675
@ -1908,7 +2162,7 @@ z
<use xlink:href="#DejaVuSans-30" transform="translate(977.255859 0.78125)"/>
</g>
</g>
<g id="line2d_31">
<g id="line2d_34">
<path d="M 394.72 60.18925
L 402.72 60.18925
L 410.72 60.18925
@ -1980,7 +2234,7 @@ z
<use xlink:href="#DejaVuSans-30" transform="translate(938.925781 0.78125)"/>
</g>
</g>
<g id="line2d_32">
<g id="line2d_35">
<path d="M 394.72 71.93175
L 402.72 71.93175
L 410.72 71.93175
@ -2008,6 +2262,177 @@ L 410.72 71.93175
</g>
</g>
</g>
<g id="text_24">
<!-- Z = 0 € -->
<g style="fill: #7c3aed" transform="translate(130.162252 327.737673) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-5a" d="M 359 4666
L 4025 4666
L 4025 4184
L 1075 531
L 4097 531
L 4097 0
L 288 0
L 288 481
L 3238 4134
L 359 4134
L 359 4666
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-3d" d="M 678 2906
L 4684 2906
L 4684 2381
L 678 2381
L 678 2906
z
M 678 1631
L 4684 1631
L 4684 1100
L 678 1100
L 678 1631
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-20ac" d="M 3647 4306
L 3647 3641
Q 3363 3969 3098 4109
Q 2834 4250 2516 4250
Q 2025 4250 1709 3937
Q 1394 3625 1294 3041
L 3097 3041
L 2925 2656
L 1244 2656
Q 1238 2581 1236 2509
Q 1234 2438 1234 2328
Q 1234 2228 1236 2156
Q 1238 2084 1244 2009
L 2638 2009
L 2463 1625
L 1294 1625
Q 1394 1041 1709 725
Q 2025 409 2516 409
Q 2834 409 3098 550
Q 3363 691 3647 1019
L 3647 359
Q 3369 134 3080 21
Q 2791 -91 2491 -91
Q 1750 -91 1267 362
Q 784 816 659 1625
L 0 1625
L 172 2009
L 606 2009
Q 606 2081 604 2154
Q 603 2228 603 2328
Q 603 2438 604 2511
Q 606 2584 606 2656
L 0 2656
L 172 3041
L 659 3041
Q 784 3844 1268 4297
Q 1753 4750 2491 4750
Q 2797 4750 3086 4639
Q 3375 4528 3647 4306
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(311.279297 0)"/>
</g>
</g>
<g id="text_25">
<!-- Z = 2.000 € -->
<g style="fill: #7c3aed" transform="translate(436.491667 273.094902) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-2e" d="M 684 794
L 1344 794
L 1344 0
L 684 0
L 684 794
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(311.279297 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(374.902344 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(438.525391 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(502.148438 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(533.935547 0)"/>
</g>
</g>
<g id="text_26">
<!-- Z = 2.375 € -->
<g style="fill: #7c3aed" transform="translate(436.491667 236.248852) scale(0.075 -0.075)">
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-33" transform="translate(311.279297 0)"/>
<use xlink:href="#DejaVuSans-37" transform="translate(374.902344 0)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(438.525391 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(502.148438 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(533.935547 0)"/>
</g>
</g>
<g id="patch_6">
<path d="M 179.285106 252.657412
Q 202.992666 226.748642 225.039768 202.654502
" style="fill: none; stroke: #7c3aed; stroke-width: 2.2; stroke-linecap: round"/>
<path d="M 220.863976 204.255364
L 225.039768 202.654502
L 223.814983 206.955653
z
" style="fill: #7c3aed; stroke: #7c3aed; stroke-width: 2.2; stroke-linecap: round"/>
</g>
<g id="text_27">
<!-- $\mathbf{c} = (150; 250)$ -->
<g style="fill: #7c3aed" transform="translate(233.66775 201.711903) scale(0.085 -0.085)">
<defs>
<path id="DejaVuSans-Bold-63" d="M 3366 3391
L 3366 2478
Q 3138 2634 2908 2709
Q 2678 2784 2431 2784
Q 1963 2784 1702 2511
Q 1441 2238 1441 1747
Q 1441 1256 1702 982
Q 1963 709 2431 709
Q 2694 709 2930 787
Q 3166 866 3366 1019
L 3366 103
Q 3103 6 2833 -42
Q 2563 -91 2291 -91
Q 1344 -91 809 395
Q 275 881 275 1747
Q 275 2613 809 3098
Q 1344 3584 2291 3584
Q 2566 3584 2833 3536
Q 3100 3488 3366 3391
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-Bold-63" transform="translate(0 0.125)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(78.759766 0.125)"/>
<use xlink:href="#DejaVuSans-28" transform="translate(182.03125 0.125)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(221.044922 0.125)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(284.667969 0.125)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(348.291016 0.125)"/>
<use xlink:href="#DejaVuSans-3b" transform="translate(411.914062 0.125)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(465.087891 0.125)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(528.710938 0.125)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(592.333984 0.125)"/>
<use xlink:href="#DejaVuSans-29" transform="translate(655.957031 0.125)"/>
</g>
</g>
</g>
</g>
<defs>

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

View file

@ -44,15 +44,25 @@ L 275.546558 234.022071
L 399.362509 260.929849
L 469.719463 263.503636
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 5.18,2.24; stroke-dashoffset: 0; stroke: #4338ca; stroke-width: 1.4"/>
</g>
<g id="line2d_2">
<path d="M 57.44 85.651402
L 363.250105 317.8
" clip-path="url(#pc5415b3683)" style="fill: none; stroke: #4338ca; stroke-opacity: 0.75; stroke-width: 1.1; stroke-linecap: square"/>
</g>
<g id="line2d_3">
<path d="M 57.44 168.507483
L 489.351818 168.507483
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 4.44,1.92; stroke-dashoffset: 0; stroke: #059669; stroke-opacity: 0.9; stroke-width: 1.2"/>
</g>
<g id="matplotlib.axis_1">
<g id="xtick_1">
<g id="line2d_2">
<g id="line2d_4">
<path d="M 94.584785 274.84
L 94.584785 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_3">
<g id="line2d_5">
<defs>
<path id="mf0a04b41f8" d="M 0 0
L 0 3.5
@ -173,12 +183,12 @@ z
</g>
</g>
<g id="xtick_2">
<g id="line2d_4">
<g id="line2d_6">
<path d="M 156.031907 274.84
L 156.031907 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_5">
<g id="line2d_7">
<g>
<use xlink:href="#mf0a04b41f8" x="156.031907" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -236,12 +246,12 @@ z
</g>
</g>
<g id="xtick_3">
<g id="line2d_6">
<g id="line2d_8">
<path d="M 217.479028 274.84
L 217.479028 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_7">
<g id="line2d_9">
<g>
<use xlink:href="#mf0a04b41f8" x="217.479028" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -279,12 +289,12 @@ z
</g>
</g>
<g id="xtick_4">
<g id="line2d_8">
<g id="line2d_10">
<path d="M 278.92615 274.84
L 278.92615 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_9">
<g id="line2d_11">
<g>
<use xlink:href="#mf0a04b41f8" x="278.92615" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -327,12 +337,12 @@ z
</g>
</g>
<g id="xtick_5">
<g id="line2d_10">
<g id="line2d_12">
<path d="M 340.373272 274.84
L 340.373272 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_11">
<g id="line2d_13">
<g>
<use xlink:href="#mf0a04b41f8" x="340.373272" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -349,12 +359,12 @@ L 340.373272 25.44
</g>
</g>
<g id="xtick_6">
<g id="line2d_12">
<g id="line2d_14">
<path d="M 401.820393 274.84
L 401.820393 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_13">
<g id="line2d_15">
<g>
<use xlink:href="#mf0a04b41f8" x="401.820393" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -371,12 +381,12 @@ L 401.820393 25.44
</g>
</g>
<g id="xtick_7">
<g id="line2d_14">
<g id="line2d_16">
<path d="M 463.267515 274.84
L 463.267515 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_15">
<g id="line2d_17">
<g>
<use xlink:href="#mf0a04b41f8" x="463.267515" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -728,12 +738,12 @@ z
</g>
<g id="matplotlib.axis_2">
<g id="ytick_1">
<g id="line2d_16">
<g id="line2d_18">
<path d="M 57.44 241.509453
L 489.351818 241.509453
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_17">
<g id="line2d_19">
<defs>
<path id="mc0b9ff744f" d="M 0 0
L -3.5 0
@ -781,12 +791,12 @@ z
</g>
</g>
<g id="ytick_2">
<g id="line2d_18">
<g id="line2d_20">
<path d="M 57.44 194.713318
L 489.351818 194.713318
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_19">
<g id="line2d_21">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="194.713318" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -802,12 +812,12 @@ L 489.351818 194.713318
</g>
</g>
<g id="ytick_3">
<g id="line2d_20">
<g id="line2d_22">
<path d="M 57.44 147.917184
L 489.351818 147.917184
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_21">
<g id="line2d_23">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="147.917184" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -823,12 +833,12 @@ L 489.351818 147.917184
</g>
</g>
<g id="ytick_4">
<g id="line2d_22">
<g id="line2d_24">
<path d="M 57.44 101.121049
L 489.351818 101.121049
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_23">
<g id="line2d_25">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="101.121049" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -844,12 +854,12 @@ L 489.351818 101.121049
</g>
</g>
<g id="ytick_5">
<g id="line2d_24">
<g id="line2d_26">
<path d="M 57.44 54.324914
L 489.351818 54.324914
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_25">
<g id="line2d_27">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="54.324914" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -1066,6 +1076,11 @@ z
</g>
</g>
</g>
<g id="line2d_28">
<path d="M 57.44 52.148669
L 407.383365 317.8
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 1.6,2.64; stroke-dashoffset: 0; stroke: #b45309; stroke-width: 1.6"/>
</g>
<g id="patch_3">
<path d="M 57.44 274.84
L 57.44 25.44
@ -1406,7 +1421,7 @@ z
</g>
</g>
<g id="legend_1">
<g id="line2d_26">
<g id="line2d_29">
<path d="M 309.988068 35.91875
L 317.988068 35.91875
L 325.988068 35.91875
@ -1778,6 +1793,341 @@ z
</g>
</g>
</g>
<g id="text_29">
<!-- dieselbe Steigung, eine Stufe höher — -->
<g style="fill: #b45309" transform="translate(222.719845 134.109124) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-64" d="M 2906 2969
L 2906 4863
L 3481 4863
L 3481 0
L 2906 0
L 2906 525
Q 2725 213 2448 61
Q 2172 -91 1784 -91
Q 1150 -91 751 415
Q 353 922 353 1747
Q 353 2572 751 3078
Q 1150 3584 1784 3584
Q 2172 3584 2448 3432
Q 2725 3281 2906 2969
z
M 947 1747
Q 947 1113 1208 752
Q 1469 391 1925 391
Q 2381 391 2643 752
Q 2906 1113 2906 1747
Q 2906 2381 2643 2742
Q 2381 3103 1925 3103
Q 1469 3103 1208 2742
Q 947 2381 947 1747
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-53" d="M 3425 4513
L 3425 3897
Q 3066 4069 2747 4153
Q 2428 4238 2131 4238
Q 1616 4238 1336 4038
Q 1056 3838 1056 3469
Q 1056 3159 1242 3001
Q 1428 2844 1947 2747
L 2328 2669
Q 3034 2534 3370 2195
Q 3706 1856 3706 1288
Q 3706 609 3251 259
Q 2797 -91 1919 -91
Q 1588 -91 1214 -16
Q 841 59 441 206
L 441 856
Q 825 641 1194 531
Q 1563 422 1919 422
Q 2459 422 2753 634
Q 3047 847 3047 1241
Q 3047 1584 2836 1778
Q 2625 1972 2144 2069
L 1759 2144
Q 1053 2284 737 2584
Q 422 2884 422 3419
Q 422 4038 858 4394
Q 1294 4750 2059 4750
Q 2388 4750 2728 4690
Q 3069 4631 3425 4513
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-66" d="M 2375 4863
L 2375 4384
L 1825 4384
Q 1516 4384 1395 4259
Q 1275 4134 1275 3809
L 1275 3500
L 2222 3500
L 2222 3053
L 1275 3053
L 1275 0
L 697 0
L 697 3053
L 147 3053
L 147 3500
L 697 3500
L 697 3744
Q 697 4328 969 4595
Q 1241 4863 1831 4863
L 2375 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-f6" d="M 1959 3097
Q 1497 3097 1228 2736
Q 959 2375 959 1747
Q 959 1119 1226 758
Q 1494 397 1959 397
Q 2419 397 2687 759
Q 2956 1122 2956 1747
Q 2956 2369 2687 2733
Q 2419 3097 1959 3097
z
M 1959 3584
Q 2709 3584 3137 3096
Q 3566 2609 3566 1747
Q 3566 888 3137 398
Q 2709 -91 1959 -91
Q 1206 -91 779 398
Q 353 888 353 1747
Q 353 2609 779 3096
Q 1206 3584 1959 3584
z
M 2253 4850
L 2888 4850
L 2888 4219
L 2253 4219
L 2253 4850
z
M 1031 4850
L 1666 4850
L 1666 4219
L 1031 4219
L 1031 4850
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2014" d="M 313 1978
L 6088 1978
L 6088 1528
L 313 1528
L 313 1978
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-64"/>
<use xlink:href="#DejaVuSans-69" transform="translate(63.476562 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(91.259766 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(152.783203 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(204.882812 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(266.40625 0)"/>
<use xlink:href="#DejaVuSans-62" transform="translate(294.189453 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(357.666016 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(419.189453 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(450.976562 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(514.453125 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(553.662109 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(615.185547 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(642.96875 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(706.445312 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(769.824219 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(833.203125 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(896.679688 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(928.466797 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(960.253906 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1021.777344 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1049.560547 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1112.939453 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1174.462891 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(1206.25 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1269.726562 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1308.935547 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(1372.314453 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1407.519531 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1469.042969 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1500.830078 0)"/>
<use xlink:href="#DejaVuSans-f6" transform="translate(1564.208984 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1625.390625 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1688.769531 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(1750.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1791.40625 0)"/>
<use xlink:href="#DejaVuSans-2014" transform="translate(1823.193359 0)"/>
</g>
<!-- jede gewichtete Summe nimmt die untere -->
<g style="fill: #b45309" transform="translate(222.719845 142.507483) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-6a" d="M 603 3500
L 1178 3500
L 1178 -63
Q 1178 -731 923 -1031
Q 669 -1331 103 -1331
L -116 -1331
L -116 -844
L 38 -844
Q 366 -844 484 -692
Q 603 -541 603 -63
L 603 3500
z
M 603 4863
L 1178 4863
L 1178 4134
L 603 4134
L 603 4863
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-6a"/>
<use xlink:href="#DejaVuSans-65" transform="translate(27.783203 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(89.306641 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(152.783203 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(214.306641 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(246.09375 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(309.570312 0)"/>
<use xlink:href="#DejaVuSans-77" transform="translate(371.09375 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(452.880859 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(480.664062 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(535.644531 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(599.023438 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(638.232422 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(699.755859 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(738.964844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(800.488281 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(832.275391 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(895.751953 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(959.130859 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1056.542969 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1153.955078 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1215.478516 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1247.265625 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1310.644531 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1338.427734 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1435.839844 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1533.251953 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1572.460938 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(1604.248047 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1667.724609 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1695.507812 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1757.03125 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1788.818359 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1852.197266 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1915.576172 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1954.785156 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2016.308594 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2055.171875 0)"/>
</g>
</g>
<g id="text_30">
<!-- ε-Constraint: CO₂ ≤ 5.912 kg -->
<g style="fill: #059669" transform="translate(63.44 182.109124) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-3b5" d="M 1234 1888
Q 884 1963 697 2166
Q 509 2366 509 2656
Q 509 3097 866 3344
Q 1219 3588 1831 3588
Q 2069 3588 2334 3550
Q 2600 3513 2906 3438
L 2906 2916
Q 2603 3006 2353 3047
Q 2100 3088 1878 3088
Q 1506 3088 1291 2944
Q 1072 2800 1072 2600
Q 1072 2381 1284 2241
Q 1494 2103 1856 2103
L 2341 2103
L 2341 1628
L 1878 1628
Q 1450 1628 1225 1469
Q 981 1294 981 1019
Q 981 738 1244 563
Q 1509 388 1981 388
Q 2272 388 2538 444
Q 2803 503 3025 619
L 3025 84
Q 2744 -3 2481 -47
Q 2216 -91 1966 -91
Q 1222 -91 819 194
Q 416 478 416 1019
Q 416 1356 631 1584
Q 847 1813 1234 1888
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2264" d="M 4684 3175
L 1684 2309
L 4684 1453
L 4684 897
L 678 2047
L 678 2578
L 4684 3725
L 4684 3175
z
M 678 531
L 4684 531
L 4684 0
L 678 0
L 678 531
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-3b5"/>
<use xlink:href="#DejaVuSans-2d" transform="translate(54.052734 0)"/>
<use xlink:href="#DejaVuSans-43" transform="translate(90.136719 0)"/>
<use xlink:href="#DejaVuSans-6f" transform="translate(159.960938 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(221.142578 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(284.521484 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(336.621094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(375.830078 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(416.943359 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(478.222656 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(506.005859 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(569.384766 0)"/>
<use xlink:href="#DejaVuSans-3a" transform="translate(608.59375 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(642.285156 0)"/>
<use xlink:href="#DejaVuSans-43" transform="translate(674.072266 0)"/>
<use xlink:href="#DejaVuSans-4f" transform="translate(743.896484 0)"/>
<use xlink:href="#DejaVuSans-2082" transform="translate(822.607422 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(862.695312 0)"/>
<use xlink:href="#DejaVuSans-2264" transform="translate(894.482422 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(978.271484 0)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(1010.058594 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(1073.681641 0)"/>
<use xlink:href="#DejaVuSans-39" transform="translate(1105.46875 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1169.091797 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1232.714844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1296.337891 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(1328.125 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(1386.035156 0)"/>
</g>
<!-- findet genau diesen Punkt -->
<g style="fill: #059669" transform="translate(63.44 190.507483) scale(0.075 -0.075)">
<use xlink:href="#DejaVuSans-66"/>
<use xlink:href="#DejaVuSans-69" transform="translate(35.205078 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(62.988281 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(126.367188 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(189.84375 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(251.367188 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(290.576172 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(322.363281 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(385.839844 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(447.363281 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(510.742188 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(572.021484 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(635.400391 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(667.1875 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(730.664062 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(758.447266 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(819.970703 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(872.070312 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(933.59375 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(996.972656 0)"/>
<use xlink:href="#DejaVuSans-50" transform="translate(1028.759766 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1087.3125 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1150.691406 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(1214.070312 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1271.980469 0)"/>
</g>
</g>
</g>
</g>
<defs>

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 160 KiB

12
OR_HTML_04/cpsat.html generated

File diff suppressed because one or more lines are too long

13
OR_HTML_04/cvar.html generated
View file

@ -320,6 +320,11 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
========================================================================================</code></pre>
<p><strong>Die 6-Sigma-Zeile setzt die Sache ins Verhältnis:</strong> Unter Normalverteilung wäre ein solcher Tag ein Ereignis von einmal in <strong>zwei Millionen Jahren</strong>. Unter der t-Verteilung mit drei Freiheitsgraden — die realen Aktienrenditen deutlich näher kommt — passiert er etwa alle <strong>zwei Jahre</strong>. Der Faktor beträgt fast <strong>eine Million</strong>.</p>
<p>Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen <em>Gewinn</em> von 2 €, im Portfolio einen <em>Verlust</em> von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung <strong>senkt</strong> das Risiko, wie es sein muss.</p>
<figure>
<img src="bilder_04/kap_cvar_subadditivitaet.svg" alt="Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie VaR_CVaR_Demo.py. Erzeugt von bilder_04/erzeuge_var_vs_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie <code>VaR_CVaR_Demo.py</code>. Erzeugt von <code>bilder_04/erzeuge_var_vs_cvar.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Links wechselt der Balken über null das Vorzeichen: Aus zwei gemeldeten Gewinnen wird im Portfolio ein Verlust. Rechts passiert nichts dergleichen — der Portfoliobalken ist kürzer als der Summenbalken, und genau das heißt Subadditivität. Der Grund für den Sprung links ist keine Rechenungenauigkeit, sondern die Bauart des Maßes: Eine Schwelle bei 95 % sieht ein Ereignis mit 4 % Wahrscheinlichkeit nicht — bis zwei davon zusammenkommen.</p>
<blockquote>
<p><strong>⚠️ Eine Falle bei der CVaR-Berechnung</strong> Der naheliegende Weg — „Mittelwert aller Verluste <span class="math inline">\ge</span> VaR“ — ist <strong>falsch</strong>, sobald die Verteilung <strong>Atome</strong> hat (also einzelne Werte mit positiver Wahrscheinlichkeit, wie hier die zwei möglichen Ausgänge). Der VaR liegt dann selbst auf einem Atom, und der Vergleich <code>&gt;=</code> erfasst zu viel Wahrscheinlichkeitsmasse. Im Beispiel oben liefert dieser naive Schätzer für Anleihe A den Wert 2,08 statt der korrekten 79,59 — ein Fehler um Faktor 38.</p>
<p>Die Rockafellar-Uryasev-Formel behandelt Atome von sich aus korrekt. Verwenden Sie sie auch dann, wenn Sie „nur schnell“ einen CVaR ausrechnen wollen.</p>
@ -327,8 +332,8 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
<hr />
<h2 id="sec:cvar-value-at-risk-und-conditional-value">20.4 Value at Risk und Conditional Value at Risk</h2>
<figure>
<img src="bilder_04/kap_cvar_var_vergleich.svg" alt="Abb. 20.1: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.1: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (<span class="math inline">t</span>-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von <code>bilder_04/erzeuge_var_cvar.py</code>.</figcaption>
<img src="bilder_04/kap_cvar_var_vergleich.svg" alt="Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (<span class="math inline">t</span>-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von <code>bilder_04/erzeuge_var_cvar.py</code>.</figcaption>
</figure>
<p><strong>Value at Risk (<span class="math inline">\text{VaR}_\alpha</span>):</strong> Der Verlust, der mit Wahrscheinlichkeit <span class="math inline">\alpha</span> nicht überschritten wird.</p>
<p><strong>Conditional Value at Risk (<span class="math inline">\text{CVaR}_\alpha</span>, auch <em>Expected Shortfall</em>):</strong> Der <strong>durchschnittliche Verlust in den schlimmsten <span class="math inline">(1-\alpha)</span> Prozent</strong> der Fälle.</p>
@ -456,8 +461,8 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
= \lVert \mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1
</span></p>
<figure>
<img src="bilder_04/kap13_cvar_zielfunktion.svg" alt="Abb. 20.2: CVaR-Zielfunktion mit Reibungs-Penalty" />
<figcaption aria-hidden="true">Abb. 20.2: CVaR-Zielfunktion mit Reibungs-Penalty</figcaption>
<img src="bilder_04/kap13_cvar_zielfunktion.svg" alt="Abb. 20.3: CVaR-Zielfunktion mit Reibungs-Penalty" />
<figcaption aria-hidden="true">Abb. 20.3: CVaR-Zielfunktion mit Reibungs-Penalty</figcaption>
</figure>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> Die <span class="math inline">L_1</span>-Norm ist die Summe der <strong>Beträge</strong>. Sie misst, wie viel Prozent des Portfolios insgesamt bewegt werden — Käufe und Verkäufe zusammen.</p>

View file

@ -415,6 +415,11 @@ f(\theta\mathbf{x} + (1-\theta)\mathbf{y}) \le \theta f(\mathbf{x}) + (1-\theta)
<blockquote>
<p><strong>🎯 Merksatz</strong> Konvex heißt: Wer bergab geht, kommt am tiefsten Punkt an. Nicht-konvex heißt: Wer bergab geht, kommt <em>irgendwo</em> an — und weiß nicht, ob es der tiefste Punkt war.</p>
</blockquote>
<figure>
<img src="bilder_04/kap02_konvexitaet_landschaft.svg" alt="Abb. 2.3: Die Funktion f(x) = x^2 + 3\sin(3x), links eindimensional mit den fünf Startpunkten aus Konvexitaet_Demo.py, rechts als separable Fortsetzung f(x_1,x_2) = f(x_1) + f(x_2) — auf jeder Achse liegt genau die Kurve von links. Erzeugt von bilder_04/erzeuge_3d_konvexitaet.py." />
<figcaption aria-hidden="true">Abb. 2.3: Die Funktion <span class="math inline">f(x) = x^2 + 3\sin(3x)</span>, links eindimensional mit den fünf Startpunkten aus <code>Konvexitaet_Demo.py</code>, rechts als separable Fortsetzung <span class="math inline">f(x_1,x_2) = f(x_1) + f(x_2)</span> — auf jeder Achse liegt genau die Kurve von links. Erzeugt von <code>bilder_04/erzeuge_3d_konvexitaet.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Links sind es fünf Läufe desselben Verfahrens, die in vier verschiedenen Tälern enden — dieselben vier, die Teil 2 von <code>Konvexitaet_Demo.py</code> weiter unten auflistet. In zwei Dimensionen werden aus den Tälern Mulden in einem Gitter, und der Unterschied zwischen Start A und Start B beträgt hier bereits mehr als 24 Einheiten. Keiner der beiden Läufe hat etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.</p>
<h3 id="was-ist-konvex-was-nicht">Was ist konvex, was nicht?</h3>
<table>
<colgroup>
@ -777,8 +782,8 @@ Preis der Ganzzahligkeit: 75.00 EUR (3.16 %)
<p><strong>Wenn meine Eingabedaten leicht ungenau sind — wie ungenau ist dann meine Lösung?</strong></p>
</blockquote>
<figure>
<img src="bilder_04/kap_fundament_kondition.svg" alt="Abb. 2.3: Was \kappa(\mathbf{A}) praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — \kappa ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von bilder_04/erzeuge_kondition.py, gerechnet mit derselben Instanz wie Skalierung_Kondition.py." />
<figcaption aria-hidden="true">Abb. 2.3: Was <span class="math inline">\kappa(\mathbf{A})</span> praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — <span class="math inline">\kappa</span> ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von <code>bilder_04/erzeuge_kondition.py</code>, gerechnet mit derselben Instanz wie <code>Skalierung_Kondition.py</code>.</figcaption>
<img src="bilder_04/kap_fundament_kondition.svg" alt="Abb. 2.4: Was \kappa(\mathbf{A}) praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — \kappa ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von bilder_04/erzeuge_kondition.py, gerechnet mit derselben Instanz wie Skalierung_Kondition.py." />
<figcaption aria-hidden="true">Abb. 2.4: Was <span class="math inline">\kappa(\mathbf{A})</span> praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — <span class="math inline">\kappa</span> ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von <code>bilder_04/erzeuge_kondition.py</code>, gerechnet mit derselben Instanz wie <code>Skalierung_Kondition.py</code>.</figcaption>
</figure>
<p>Die Antwort trägt einen Namen: die <strong>Konditionszahl</strong> <span class="math inline">\kappa(\mathbf{A})</span>. Sie ist der wichtigste Begriff dieses Kapitels für den Produktivbetrieb, weil er erklärt, warum ein mathematisch korrektes Modell trotzdem unbrauchbare Ergebnisse liefern kann — oder sich mit einem sachlich falschen <code>INFEASIBLE</code> verabschiedet.</p>
<h3 id="die-geometrische-vorstellung-zwei-fast-parallele-linien">Die geometrische Vorstellung: zwei fast parallele Linien</h3>

View file

@ -4767,6 +4767,11 @@ f(\theta\mathbf{x} + (1-\theta)\mathbf{y}) \le \theta f(\mathbf{x}) + (1-\theta)
<blockquote>
<p><strong>🎯 Merksatz</strong> Konvex heißt: Wer bergab geht, kommt am tiefsten Punkt an. Nicht-konvex heißt: Wer bergab geht, kommt <em>irgendwo</em> an — und weiß nicht, ob es der tiefste Punkt war.</p>
</blockquote>
<figure>
<img src="bilder_04/kap02_konvexitaet_landschaft.svg" alt="Abb. 2.3: Die Funktion f(x) = x^2 + 3\sin(3x), links eindimensional mit den fünf Startpunkten aus Konvexitaet_Demo.py, rechts als separable Fortsetzung f(x_1,x_2) = f(x_1) + f(x_2) — auf jeder Achse liegt genau die Kurve von links. Erzeugt von bilder_04/erzeuge_3d_konvexitaet.py." />
<figcaption aria-hidden="true">Abb. 2.3: Die Funktion <span class="math inline">f(x) = x^2 + 3\sin(3x)</span>, links eindimensional mit den fünf Startpunkten aus <code>Konvexitaet_Demo.py</code>, rechts als separable Fortsetzung <span class="math inline">f(x_1,x_2) = f(x_1) + f(x_2)</span> — auf jeder Achse liegt genau die Kurve von links. Erzeugt von <code>bilder_04/erzeuge_3d_konvexitaet.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Links sind es fünf Läufe desselben Verfahrens, die in vier verschiedenen Tälern enden — dieselben vier, die Teil 2 von <code>Konvexitaet_Demo.py</code> weiter unten auflistet. In zwei Dimensionen werden aus den Tälern Mulden in einem Gitter, und der Unterschied zwischen Start A und Start B beträgt hier bereits mehr als 24 Einheiten. Keiner der beiden Läufe hat etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.</p>
<h3 id="was-ist-konvex-was-nicht">Was ist konvex, was nicht?</h3>
<table>
<colgroup>
@ -5129,8 +5134,8 @@ Preis der Ganzzahligkeit: 75.00 EUR (3.16 %)
<p><strong>Wenn meine Eingabedaten leicht ungenau sind — wie ungenau ist dann meine Lösung?</strong></p>
</blockquote>
<figure>
<img src="bilder_04/kap_fundament_kondition.svg" alt="Abb. 2.3: Was \kappa(\mathbf{A}) praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — \kappa ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von bilder_04/erzeuge_kondition.py, gerechnet mit derselben Instanz wie Skalierung_Kondition.py." />
<figcaption aria-hidden="true">Abb. 2.3: Was <span class="math inline">\kappa(\mathbf{A})</span> praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — <span class="math inline">\kappa</span> ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von <code>bilder_04/erzeuge_kondition.py</code>, gerechnet mit derselben Instanz wie <code>Skalierung_Kondition.py</code>.</figcaption>
<img src="bilder_04/kap_fundament_kondition.svg" alt="Abb. 2.4: Was \kappa(\mathbf{A}) praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — \kappa ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von bilder_04/erzeuge_kondition.py, gerechnet mit derselben Instanz wie Skalierung_Kondition.py." />
<figcaption aria-hidden="true">Abb. 2.4: Was <span class="math inline">\kappa(\mathbf{A})</span> praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — <span class="math inline">\kappa</span> ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von <code>bilder_04/erzeuge_kondition.py</code>, gerechnet mit derselben Instanz wie <code>Skalierung_Kondition.py</code>.</figcaption>
</figure>
<p>Die Antwort trägt einen Namen: die <strong>Konditionszahl</strong> <span class="math inline">\kappa(\mathbf{A})</span>. Sie ist der wichtigste Begriff dieses Kapitels für den Produktivbetrieb, weil er erklärt, warum ein mathematisch korrektes Modell trotzdem unbrauchbare Ergebnisse liefern kann — oder sich mit einem sachlich falschen <code>INFEASIBLE</code> verabschiedet.</p>
<h3 id="die-geometrische-vorstellung-zwei-fast-parallele-linien">Die geometrische Vorstellung: zwei fast parallele Linien</h3>
@ -8065,9 +8070,10 @@ Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt
<h2 id="sec:lp-der-simplex-algorithmus-schritt-fuer-schritt">5.4 Der Simplex-Algorithmus Schritt für Schritt</h2>
<p>Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus <a href="#kap-fundament">Kapitel 2</a>: <strong>Das Optimum liegt in einer Ecke.</strong> Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.</p>
<figure>
<img src="bilder_04/kap_lp_simplex_ecken.svg" alt="Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von bilder_04/erzeuge_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von <code>bilder_04/erzeuge_polyeder.py</code>.</figcaption>
<img src="bilder_04/kap_lp_simplex_ecken.svg" alt="Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient \mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient <span class="math inline">\mathbf{c}</span>. Erzeugt von <code>bilder_04/erzeuge_polyeder.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.</p>
<h3 id="der-ablauf">Der Ablauf</h3>
<ol type="1">
<li><strong>Initialisierung.</strong> Starte an einer bekannten Ecke — meist dem Ursprung <span class="math inline">\mathbf{x} = \mathbf{0}</span>, bei dem alle Schlupfvariablen ihre Kapazität aufnehmen: <span class="math inline">\mathbf{s} = \mathbf{b}</span>. Das ist zulässig, solange <span class="math inline">\mathbf{b} \ge \mathbf{0}</span>.</li>
@ -8551,6 +8557,11 @@ Optimum nach 2 Pivotschritten erreicht.
<p><strong>Satz.</strong> Besitzt das primale Problem eine endliche Optimallösung <span class="math inline">\mathbf{x}^*</span>, so besitzt auch das duale eine Optimallösung <span class="math inline">\mathbf{y}^*</span>, und die Zielwerte sind <strong>exakt gleich</strong>: <span class="math display">\mathbf{c}^\top\mathbf{x}^* = \mathbf{b}^\top\mathbf{y}^*</span></p>
</blockquote>
<p>Prüfen wir das an unserem Beispiel: <span class="math inline">\mathbf{b}^\top\mathbf{y}^* = 40 \cdot 12{,}5 + 60 \cdot 31{,}25 + 8 \cdot 0 = 500 + 1875 + 0 = 2375</span> ✓ — identisch mit <span class="math inline">Z^* = 2375</span>.</p>
<figure>
<img src="bilder_04/kap_lp_dualitaets_bruecke.svg" alt="Abb. 5.3: Beide Probleme geometrisch, gerechnet mit Dualitaet_Nachweis.py. Rechts ist die Ebene y_3 = 0 gezeichnet — zulässig, weil das Marktlimit x_1 \le 8 bei x_1^* = 7{,}5 nicht bindet und deshalb y_3^* = 0 gilt. Erzeugt von bilder_04/erzeuge_dualitaets_bruecke.py." />
<figcaption aria-hidden="true">Abb. 5.3: Beide Probleme geometrisch, gerechnet mit <code>Dualitaet_Nachweis.py</code>. Rechts ist die Ebene <span class="math inline">y_3 = 0</span> gezeichnet — zulässig, weil das Marktlimit <span class="math inline">x_1 \le 8</span> bei <span class="math inline">x_1^* = 7{,}5</span> nicht bindet und deshalb <span class="math inline">y_3^* = 0</span> gilt. Erzeugt von <code>bilder_04/erzeuge_dualitaets_bruecke.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Die beiden Tafeln haben nichts gemeinsam: verschiedene Achsen, verschiedene Einheiten, links ein geschlossenes Polyeder, rechts ein nach oben offener Bereich. Links wird maximiert, rechts minimiert. Und doch steht an beiden Optima dieselbe Zahl — 2.375 €. Genau das behauptet der Satz, und mehr ist an ihm nicht zu sehen.</p>
<p>Das lässt sich auch unabhängig von der Handrechnung nachweisen: Primal- und Dualproblem werden als <strong>zwei getrennte, voneinander unabhängige LPs</strong> aufgestellt und gelöst — wenn der starke Dualitätssatz stimmt, müssen beide denselben Zielwert liefern, ohne dass eines vom anderen „weiß“.</p>
<div class="sourceCode" id="cb52"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a></span>
@ -8939,8 +8950,8 @@ Pruefung: komplementaerer Schlupf fuer alle Ressourcen erfuellt.
<h2 id="sec:lp-entartung">5.9 Wann Schattenpreise lügen: Entartung und Toleranzen</h2>
<p>Der vorige Abschnitt hat gezeigt, wie wertvoll Schattenpreise sind. Dieser zeigt die zwei Fälle, in denen sie eine Entscheidung <strong>nicht</strong> tragen — und wie man beide erkennt, bevor jemand auf ihrer Grundlage eine Maschine kauft.</p>
<figure>
<img src="bilder_04/kap_lp_entartung.svg" alt="Abb. 5.3: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von bilder_04/erzeuge_entartung_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.3: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von <code>bilder_04/erzeuge_entartung_polyeder.py</code>.</figcaption>
<img src="bilder_04/kap_lp_entartung.svg" alt="Abb. 5.4: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von bilder_04/erzeuge_entartung_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.4: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von <code>bilder_04/erzeuge_entartung_polyeder.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Jeder Wert innerhalb der farbigen Balken erfüllt die Optimalitätsbedingungen. Zwei Solver dürfen also verschiedene Schattenpreise melden und haben beide recht — die Frage „was ist eine Stunde wert?” hat hier schlicht keine eindeutige Antwort.</p>
<h3 id="fall-1-entartung">Fall 1: Entartung</h3>
@ -9676,6 +9687,11 @@ Loesung eine Nebenbedingung verletzt. &#39;Verlust&#39; = Abstand zum exakten Op
<li>oder die LP-Lösung bereits ganzzahlig ist (dann ist dieser Ast fertig).</li>
</ul></li>
</ol>
<figure>
<img src="bilder_04/kap_milp_bnb_geometrie.svg" alt="Abb. 6.3: Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die 13 ganzzahligen zulässigen Punkte. Erzeugt von bilder_04/erzeuge_bnb_geometrie.py." />
<figcaption aria-hidden="true">Abb. 6.3: Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die 13 ganzzahligen zulässigen Punkte. Erzeugt von <code>bilder_04/erzeuge_bnb_geometrie.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Der rote Streifen ist genau das, was Phase 2 wegnimmt — und in ihm liegt kein einziger grauer Punkt. Deshalb ist Verzweigen verlustfrei. Weggenommen wird trotzdem etwas Wertvolles: das LP-Optimum (3; 1,5) selbst. Genau darum sinkt die Schranke von 21 auf 20,67, und genau dieses Sinken ist der Fortschritt, den der Suchbaum Knoten für Knoten protokolliert.</p>
<p>Zusätzlich fügen Solver <strong>Schnittebenen</strong> (<em>cutting planes</em>, z. B. Gomory-Cuts) hinzu: gültige Ungleichungen, die gebrochene Bereiche wegschneiden, ohne einen einzigen zulässigen ganzzahligen Punkt zu entfernen.</p>
<blockquote>
<p><strong>✏️ Handrechnung 6.2: Ein Suchbaum von Hand</strong></p>
@ -9690,8 +9706,8 @@ Loesung eine Nebenbedingung verletzt. &#39;Verlust&#39; = Abstand zum exakten Op
<h2 id="sec:milp-modellierungstricks-big-m-und-logische-bedingungen">6.5 Modellierungstricks: Big-M und logische Bedingungen</h2>
<p>Hier kommt das Handwerkszeug, das MILP so mächtig macht: Man übersetzt Geschäftsregeln in lineare Ungleichungen — mithilfe binärer Hilfsvariablen <span class="math inline">y \in \{0,1\}</span> und einer hinreichend großen Konstante <span class="math inline">M</span>.</p>
<figure>
<img src="bilder_04/kap_milp_big_m.svg" alt="Abb. 6.3: Dieselbe Bedingung x \le M\,y, zwei Zahlen für M. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor y ganzzahlig wird. Erzeugt von bilder_04/erzeuge_big_m_vergleich.py." />
<figcaption aria-hidden="true">Abb. 6.3: Dieselbe Bedingung <span class="math inline">x \le M\,y</span>, zwei Zahlen für <span class="math inline">M</span>. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor <span class="math inline">y</span> ganzzahlig wird. Erzeugt von <code>bilder_04/erzeuge_big_m_vergleich.py</code>.</figcaption>
<img src="bilder_04/kap_milp_big_m.svg" alt="Abb. 6.4: Dieselbe Bedingung x \le M\,y, zwei Zahlen für M. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor y ganzzahlig wird. Erzeugt von bilder_04/erzeuge_big_m_vergleich.py." />
<figcaption aria-hidden="true">Abb. 6.4: Dieselbe Bedingung <span class="math inline">x \le M\,y</span>, zwei Zahlen für <span class="math inline">M</span>. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor <span class="math inline">y</span> ganzzahlig wird. Erzeugt von <code>bilder_04/erzeuge_big_m_vergleich.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Für <span class="math inline">y \in \{0,1\}</span> sind beide Modelle gleichwertig; die beiden blauen Punkte stimmen überein. Verschieden ist nur, was dazwischen erlaubt ist — und rechts genügt bereits <span class="math inline">y = 0{,}08</span>, um die volle Kapazität freizugeben.</p>
<h3 id="muster-1-fixkosten-aktivierungsschalter">Muster 1 — Fixkosten / Aktivierungsschalter</h3>
@ -11199,6 +11215,11 @@ Fr -&gt; Abel</code></pre>
<p>Aus <span class="math inline">4 \times 4 = 16</span> Kombinationen sind ohne jedes Ausprobieren <strong>9</strong> geworden. Bei tausenden verketteten Bedingungen wirkt dieser Effekt lawinenartig — das ist der Kern der Leistungsfähigkeit von CP.</p>
<p><strong>Und wenn ein Widerspruch auftritt?</strong> Dann lernt der SAT-Kern eine <strong>Sperrklausel</strong> (<em>nogood</em>): „Diese Kombination nie wieder probieren.“ Der Solver springt dann nicht nur einen Schritt zurück, sondern direkt zur Ursache des Konflikts (<em>non-chronological backtracking</em>).</p>
</blockquote>
<figure>
<img src="bilder_04/kap_cpsat_propagation_ablauf.svg" alt="Abb. 7.2: Dieselbe Handrechnung als Bild: grün die Werte, die in der Domain bleiben, rot der Wert, den die jeweilige Runde streicht, grau die schon vorher gestrichenen. Erzeugt von bilder_04/erzeuge_propagation_ablauf.py." />
<figcaption aria-hidden="true">Abb. 7.2: Dieselbe Handrechnung als Bild: grün die Werte, die in der Domain bleiben, rot der Wert, den die jeweilige Runde streicht, grau die schon vorher gestrichenen. Erzeugt von <code>bilder_04/erzeuge_propagation_ablauf.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Jede Runde streicht genau <strong>einen</strong> Wert — und trotzdem sinkt die Zahl der Kombinationen von 16 auf 9, also um gut vierzig Prozent. Das ist der Hebel: Gestrichen wird nicht eine Kombination, sondern ein Wert, und mit ihm alle Kombinationen, in denen er vorkam.</p>
<div class="sourceCode" id="cb77"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb77-3"><a href="#cb77-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Propagation_Demo.py</span></span>
@ -11839,11 +11860,12 @@ Untere Schranke (laengster Auftrag / hoechste Maschinenlast): 10
Erreichter Makespan: 11
==========================================================================</code></pre>
<figure>
<img src="bilder_04/kap06_jobshop_gantt.svg" alt="Abb. 7.2: Job-Shop-Plan als Gantt-Diagramm" />
<figcaption aria-hidden="true">Abb. 7.2: Job-Shop-Plan als Gantt-Diagramm</figcaption>
<img src="bilder_04/kap06_jobshop_gantt.svg" alt="Abb. 7.3: Derselbe Plan als Gantt-Diagramm. Schraffiert die beiden Stillstände zwischen zwei Arbeitsgängen, gestrichelt die untere Schranke. Erzeugt von bilder_04/erzeuge_kap06_gantt.py." />
<figcaption aria-hidden="true">Abb. 7.3: Derselbe Plan als Gantt-Diagramm. Schraffiert die beiden Stillstände zwischen zwei Arbeitsgängen, gestrichelt die untere Schranke. Erzeugt von <code>bilder_04/erzeuge_kap06_gantt.py</code>.</figcaption>
</figure>
<p><em>Eine interaktive Fassung dieser Grafik steht auf den Kapitelseiten der Website bereit.</em></p>
<p><strong>Lesen Sie das Gantt-Diagramm.</strong> Auftrag 1 kommt zuerst an die Fräse (02), Auftrag 0 muss warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig ist die Lücke in der Dreherei bei Zeitpunkt 4: Die Maschine steht still, obwohl Auftrag 0 schon Arbeit hätte — er ist aber bis 5 noch an der Fräse. Dieses <strong>Warten ist unvermeidlich</strong>; es entsteht aus dem Zusammenspiel von Reihenfolge- und Maschinenbedingungen, nicht aus einer schlechten Planung.</p>
<p><strong>Lesen Sie das Gantt-Diagramm.</strong> Auftrag 1 kommt zuerst an die Fräse (02), Auftrag 0 muss warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig sind die beiden schraffierten Lücken. Die in der Dreherei bei Zeitpunkt 4 ist die sprechendere: Die Maschine steht still, obwohl Auftrag 0 schon Arbeit hätte — er ist aber bis 5 noch an der Fräse. Dieses <strong>Warten ist unvermeidlich</strong>; es entsteht aus dem Zusammenspiel von Reihenfolge- und Maschinenbedingungen, nicht aus einer schlechten Planung.</p>
<p>Die gestrichelte Linie bei 10 sagt dasselbe von der anderen Seite. Sie ist die untere Schranke aus dem Programm, und sie stammt von der Dreherei: Deren drei Arbeitsgänge dauern zusammen 2 + 4 + 4 = 10 Zeiteinheiten, die sich nicht überlappen dürfen. Die Dreherei ist im Plan von 0 bis 11 belegt — also genau ihre 10 Einheiten Arbeit plus die eine schraffierte Einheit Stillstand. Der Plan ist somit nicht deshalb elf lang, weil schlecht geplant wurde, sondern weil die Engpassmaschine einmal warten muss. (Die zweite Lücke, in der Lackiererei, kostet nichts: Diese Maschine ist ohnehin nicht der Engpass.)</p>
<blockquote>
<p><strong>⚠️ Mehrere gleich gute Pläne — und was das für Ihre Tests bedeutet</strong></p>
<p>Der kürzeste Makespan beträgt 11, aber es gibt <strong>mehrere verschiedene Pläne</strong>, die ihn erreichen. Welchen CP-SAT findet, hängt normalerweise davon ab, welcher seiner parallelen Suchstränge zuerst fertig wird — dasselbe Programm liefert dann von Lauf zu Lauf einen anderen (gleich guten) Plan.</p>
@ -19878,12 +19900,13 @@ dem CO2-Preis, den das Unternehmen ohnehin ansetzt.
<h2 id="sec:mehrziel-luecke">14.6 Was die gewichtete Summe nicht sieht</h2>
<p>Die Front hat <strong>zehn</strong> Punkte. Ein Raster aus 1 401 Gewichten findet <strong>sechs</strong> davon. Vier Kompromisse sind für kein Gewicht erreichbar — und das ist keine Frage der Rasterweite.</p>
<figure>
<img src="bilder_04/kap_mehrziel_pareto.svg" alt="Abb. 14.1: Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt." />
<figcaption aria-hidden="true">Abb. 14.1: Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt.</figcaption>
<img src="bilder_04/kap_mehrziel_pareto.svg" alt="Abb. 14.1: Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt. Die dünne Gerade ist eine solche Stützgerade, die punktierte die parallele Gerade durch den am weitesten abliegenden der vier Punkte. Erzeugt von bilder_04/erzeuge_pareto_front.py." />
<figcaption aria-hidden="true">Abb. 14.1: Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt. Die dünne Gerade ist eine solche Stützgerade, die punktierte die parallele Gerade durch den am weitesten abliegenden der vier Punkte. Erzeugt von <code>bilder_04/erzeuge_pareto_front.py</code>.</figcaption>
</figure>
<p><em>Eine interaktive Fassung dieser Grafik steht auf den Kapitelseiten der Website bereit.</em></p>
<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>
<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>
@ -24298,6 +24321,11 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
========================================================================================</code></pre>
<p><strong>Die 6-Sigma-Zeile setzt die Sache ins Verhältnis:</strong> Unter Normalverteilung wäre ein solcher Tag ein Ereignis von einmal in <strong>zwei Millionen Jahren</strong>. Unter der t-Verteilung mit drei Freiheitsgraden — die realen Aktienrenditen deutlich näher kommt — passiert er etwa alle <strong>zwei Jahre</strong>. Der Faktor beträgt fast <strong>eine Million</strong>.</p>
<p>Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen <em>Gewinn</em> von 2 €, im Portfolio einen <em>Verlust</em> von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung <strong>senkt</strong> das Risiko, wie es sein muss.</p>
<figure>
<img src="bilder_04/kap_cvar_subadditivitaet.svg" alt="Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie VaR_CVaR_Demo.py. Erzeugt von bilder_04/erzeuge_var_vs_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie <code>VaR_CVaR_Demo.py</code>. Erzeugt von <code>bilder_04/erzeuge_var_vs_cvar.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Links wechselt der Balken über null das Vorzeichen: Aus zwei gemeldeten Gewinnen wird im Portfolio ein Verlust. Rechts passiert nichts dergleichen — der Portfoliobalken ist kürzer als der Summenbalken, und genau das heißt Subadditivität. Der Grund für den Sprung links ist keine Rechenungenauigkeit, sondern die Bauart des Maßes: Eine Schwelle bei 95 % sieht ein Ereignis mit 4 % Wahrscheinlichkeit nicht — bis zwei davon zusammenkommen.</p>
<blockquote>
<p><strong>⚠️ Eine Falle bei der CVaR-Berechnung</strong> Der naheliegende Weg — „Mittelwert aller Verluste <span class="math inline">\ge</span> VaR“ — ist <strong>falsch</strong>, sobald die Verteilung <strong>Atome</strong> hat (also einzelne Werte mit positiver Wahrscheinlichkeit, wie hier die zwei möglichen Ausgänge). Der VaR liegt dann selbst auf einem Atom, und der Vergleich <code>&gt;=</code> erfasst zu viel Wahrscheinlichkeitsmasse. Im Beispiel oben liefert dieser naive Schätzer für Anleihe A den Wert 2,08 statt der korrekten 79,59 — ein Fehler um Faktor 38.</p>
<p>Die Rockafellar-Uryasev-Formel behandelt Atome von sich aus korrekt. Verwenden Sie sie auch dann, wenn Sie „nur schnell“ einen CVaR ausrechnen wollen.</p>
@ -24305,8 +24333,8 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
<hr />
<h2 id="sec:cvar-value-at-risk-und-conditional-value">20.4 Value at Risk und Conditional Value at Risk</h2>
<figure>
<img src="bilder_04/kap_cvar_var_vergleich.svg" alt="Abb. 20.1: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.1: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (<span class="math inline">t</span>-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von <code>bilder_04/erzeuge_var_cvar.py</code>.</figcaption>
<img src="bilder_04/kap_cvar_var_vergleich.svg" alt="Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (t-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von bilder_04/erzeuge_var_cvar.py." />
<figcaption aria-hidden="true">Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus dem Fat-Tail-Modell dieses Kapitels (<span class="math inline">t</span>-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von <code>bilder_04/erzeuge_var_cvar.py</code>.</figcaption>
</figure>
<p><strong>Value at Risk (<span class="math inline">\text{VaR}_\alpha</span>):</strong> Der Verlust, der mit Wahrscheinlichkeit <span class="math inline">\alpha</span> nicht überschritten wird.</p>
<p><strong>Conditional Value at Risk (<span class="math inline">\text{CVaR}_\alpha</span>, auch <em>Expected Shortfall</em>):</strong> Der <strong>durchschnittliche Verlust in den schlimmsten <span class="math inline">(1-\alpha)</span> Prozent</strong> der Fälle.</p>
@ -24432,8 +24460,8 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -&gt; subadditiv (kohaerent)
= \lVert \mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1
</span></p>
<figure>
<img src="bilder_04/kap13_cvar_zielfunktion.svg" alt="Abb. 20.2: CVaR-Zielfunktion mit Reibungs-Penalty" />
<figcaption aria-hidden="true">Abb. 20.2: CVaR-Zielfunktion mit Reibungs-Penalty</figcaption>
<img src="bilder_04/kap13_cvar_zielfunktion.svg" alt="Abb. 20.3: CVaR-Zielfunktion mit Reibungs-Penalty" />
<figcaption aria-hidden="true">Abb. 20.3: CVaR-Zielfunktion mit Reibungs-Penalty</figcaption>
</figure>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> Die <span class="math inline">L_1</span>-Norm ist die Summe der <strong>Beträge</strong>. Sie misst, wie viel Prozent des Portfolios insgesamt bewegt werden — Käufe und Verkäufe zusammen.</p>
@ -26611,8 +26639,8 @@ aus.
<hr />
<h2 id="sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform">22.5 Architektur einer produktionsreifen OR-Plattform</h2>
<figure>
<img src="bilder_04/kap15_or_plattform.svg" alt="Abb. 22.2: Architektur einer produktionsreifen OR-Plattform" />
<figcaption aria-hidden="true">Abb. 22.2: Architektur einer produktionsreifen OR-Plattform</figcaption>
<img src="bilder_04/kap15_or_plattform.svg" alt="Abb. 22.2: Die Schichten dieses Abschnitts als Bild — erzeugt aus der Tabelle daneben, damit beide nicht auseinanderlaufen können. Erzeugt von bilder_04/erzeuge_or_architektur.py." />
<figcaption aria-hidden="true">Abb. 22.2: Die Schichten dieses Abschnitts als Bild — erzeugt <strong>aus</strong> der Tabelle daneben, damit beide nicht auseinanderlaufen können. Erzeugt von <code>bilder_04/erzeuge_or_architektur.py</code>.</figcaption>
</figure>
<p>In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein <strong>versionierter, zustandsloser Dienst</strong>:</p>
<table>
@ -26661,6 +26689,7 @@ aus.
</tr>
</tbody>
</table>
<p><strong>Was Sie in der Abbildung sehen.</strong> Fünf der sechs Schichten bilden einen Weg: Daten gehen oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut sie am Ende auch so: als etwas, das man hinterher noch macht.</p>
<p><strong>Fünf Prinzipien, die sich in der Praxis bewährt haben:</strong></p>
<ol type="1">
<li><strong>Snapshot-Prinzip.</strong> Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.</li>

14
OR_HTML_04/lp.html generated
View file

@ -250,9 +250,10 @@
<h2 id="sec:lp-der-simplex-algorithmus-schritt-fuer-schritt">5.4 Der Simplex-Algorithmus Schritt für Schritt</h2>
<p>Der von George Dantzig 1947 formulierte Simplex-Algorithmus nutzt den Fundamentalsatz aus <a href="fundament.html#kap-fundament">Kapitel 2</a>: <strong>Das Optimum liegt in einer Ecke.</strong> Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.</p>
<figure>
<img src="bilder_04/kap_lp_simplex_ecken.svg" alt="Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von bilder_04/erzeuge_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von <code>bilder_04/erzeuge_polyeder.py</code>.</figcaption>
<img src="bilder_04/kap_lp_simplex_ecken.svg" alt="Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient \mathbf{c}. Erzeugt von bilder_04/erzeuge_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über (0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient <span class="math inline">\mathbf{c}</span>. Erzeugt von <code>bilder_04/erzeuge_polyeder.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.</p>
<h3 id="der-ablauf">Der Ablauf</h3>
<ol type="1">
<li><strong>Initialisierung.</strong> Starte an einer bekannten Ecke — meist dem Ursprung <span class="math inline">\mathbf{x} = \mathbf{0}</span>, bei dem alle Schlupfvariablen ihre Kapazität aufnehmen: <span class="math inline">\mathbf{s} = \mathbf{b}</span>. Das ist zulässig, solange <span class="math inline">\mathbf{b} \ge \mathbf{0}</span>.</li>
@ -736,6 +737,11 @@ Optimum nach 2 Pivotschritten erreicht.
<p><strong>Satz.</strong> Besitzt das primale Problem eine endliche Optimallösung <span class="math inline">\mathbf{x}^*</span>, so besitzt auch das duale eine Optimallösung <span class="math inline">\mathbf{y}^*</span>, und die Zielwerte sind <strong>exakt gleich</strong>: <span class="math display">\mathbf{c}^\top\mathbf{x}^* = \mathbf{b}^\top\mathbf{y}^*</span></p>
</blockquote>
<p>Prüfen wir das an unserem Beispiel: <span class="math inline">\mathbf{b}^\top\mathbf{y}^* = 40 \cdot 12{,}5 + 60 \cdot 31{,}25 + 8 \cdot 0 = 500 + 1875 + 0 = 2375</span> ✓ — identisch mit <span class="math inline">Z^* = 2375</span>.</p>
<figure>
<img src="bilder_04/kap_lp_dualitaets_bruecke.svg" alt="Abb. 5.3: Beide Probleme geometrisch, gerechnet mit Dualitaet_Nachweis.py. Rechts ist die Ebene y_3 = 0 gezeichnet — zulässig, weil das Marktlimit x_1 \le 8 bei x_1^* = 7{,}5 nicht bindet und deshalb y_3^* = 0 gilt. Erzeugt von bilder_04/erzeuge_dualitaets_bruecke.py." />
<figcaption aria-hidden="true">Abb. 5.3: Beide Probleme geometrisch, gerechnet mit <code>Dualitaet_Nachweis.py</code>. Rechts ist die Ebene <span class="math inline">y_3 = 0</span> gezeichnet — zulässig, weil das Marktlimit <span class="math inline">x_1 \le 8</span> bei <span class="math inline">x_1^* = 7{,}5</span> nicht bindet und deshalb <span class="math inline">y_3^* = 0</span> gilt. Erzeugt von <code>bilder_04/erzeuge_dualitaets_bruecke.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Die beiden Tafeln haben nichts gemeinsam: verschiedene Achsen, verschiedene Einheiten, links ein geschlossenes Polyeder, rechts ein nach oben offener Bereich. Links wird maximiert, rechts minimiert. Und doch steht an beiden Optima dieselbe Zahl — 2.375 €. Genau das behauptet der Satz, und mehr ist an ihm nicht zu sehen.</p>
<p>Das lässt sich auch unabhängig von der Handrechnung nachweisen: Primal- und Dualproblem werden als <strong>zwei getrennte, voneinander unabhängige LPs</strong> aufgestellt und gelöst — wenn der starke Dualitätssatz stimmt, müssen beide denselben Zielwert liefern, ohne dass eines vom anderen „weiß“.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
@ -1126,8 +1132,8 @@ Pruefung: komplementaerer Schlupf fuer alle Ressourcen erfuellt.
<h2 id="sec:lp-entartung">5.9 Wann Schattenpreise lügen: Entartung und Toleranzen</h2>
<p>Der vorige Abschnitt hat gezeigt, wie wertvoll Schattenpreise sind. Dieser zeigt die zwei Fälle, in denen sie eine Entscheidung <strong>nicht</strong> tragen — und wie man beide erkennt, bevor jemand auf ihrer Grundlage eine Maschine kauft.</p>
<figure>
<img src="bilder_04/kap_lp_entartung.svg" alt="Abb. 5.3: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von bilder_04/erzeuge_entartung_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.3: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von <code>bilder_04/erzeuge_entartung_polyeder.py</code>.</figcaption>
<img src="bilder_04/kap_lp_entartung.svg" alt="Abb. 5.4: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von bilder_04/erzeuge_entartung_polyeder.py." />
<figcaption aria-hidden="true">Abb. 5.4: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von <code>bilder_04/erzeuge_entartung_polyeder.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Jeder Wert innerhalb der farbigen Balken erfüllt die Optimalitätsbedingungen. Zwei Solver dürfen also verschiedene Schattenpreise melden und haben beide recht — die Frage „was ist eine Stunde wert?” hat hier schlicht keine eindeutige Antwort.</p>
<h3 id="fall-1-entartung">Fall 1: Entartung</h3>

File diff suppressed because one or more lines are too long

9
OR_HTML_04/milp.html generated
View file

@ -382,6 +382,11 @@ Loesung eine Nebenbedingung verletzt. &#39;Verlust&#39; = Abstand zum exakten Op
<li>oder die LP-Lösung bereits ganzzahlig ist (dann ist dieser Ast fertig).</li>
</ul></li>
</ol>
<figure>
<img src="bilder_04/kap_milp_bnb_geometrie.svg" alt="Abb. 6.3: Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die 13 ganzzahligen zulässigen Punkte. Erzeugt von bilder_04/erzeuge_bnb_geometrie.py." />
<figcaption aria-hidden="true">Abb. 6.3: Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die 13 ganzzahligen zulässigen Punkte. Erzeugt von <code>bilder_04/erzeuge_bnb_geometrie.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Der rote Streifen ist genau das, was Phase 2 wegnimmt — und in ihm liegt kein einziger grauer Punkt. Deshalb ist Verzweigen verlustfrei. Weggenommen wird trotzdem etwas Wertvolles: das LP-Optimum (3; 1,5) selbst. Genau darum sinkt die Schranke von 21 auf 20,67, und genau dieses Sinken ist der Fortschritt, den der Suchbaum Knoten für Knoten protokolliert.</p>
<p>Zusätzlich fügen Solver <strong>Schnittebenen</strong> (<em>cutting planes</em>, z. B. Gomory-Cuts) hinzu: gültige Ungleichungen, die gebrochene Bereiche wegschneiden, ohne einen einzigen zulässigen ganzzahligen Punkt zu entfernen.</p>
<blockquote>
<p><strong>✏️ Handrechnung 6.2: Ein Suchbaum von Hand</strong></p>
@ -396,8 +401,8 @@ Loesung eine Nebenbedingung verletzt. &#39;Verlust&#39; = Abstand zum exakten Op
<h2 id="sec:milp-modellierungstricks-big-m-und-logische-bedingungen">6.5 Modellierungstricks: Big-M und logische Bedingungen</h2>
<p>Hier kommt das Handwerkszeug, das MILP so mächtig macht: Man übersetzt Geschäftsregeln in lineare Ungleichungen — mithilfe binärer Hilfsvariablen <span class="math inline">y \in \{0,1\}</span> und einer hinreichend großen Konstante <span class="math inline">M</span>.</p>
<figure>
<img src="bilder_04/kap_milp_big_m.svg" alt="Abb. 6.3: Dieselbe Bedingung x \le M\,y, zwei Zahlen für M. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor y ganzzahlig wird. Erzeugt von bilder_04/erzeuge_big_m_vergleich.py." />
<figcaption aria-hidden="true">Abb. 6.3: Dieselbe Bedingung <span class="math inline">x \le M\,y</span>, zwei Zahlen für <span class="math inline">M</span>. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor <span class="math inline">y</span> ganzzahlig wird. Erzeugt von <code>bilder_04/erzeuge_big_m_vergleich.py</code>.</figcaption>
<img src="bilder_04/kap_milp_big_m.svg" alt="Abb. 6.4: Dieselbe Bedingung x \le M\,y, zwei Zahlen für M. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor y ganzzahlig wird. Erzeugt von bilder_04/erzeuge_big_m_vergleich.py." />
<figcaption aria-hidden="true">Abb. 6.4: Dieselbe Bedingung <span class="math inline">x \le M\,y</span>, zwei Zahlen für <span class="math inline">M</span>. Gezeigt ist die LP-Relaxation — also das, was der Solver sieht, bevor <span class="math inline">y</span> ganzzahlig wird. Erzeugt von <code>bilder_04/erzeuge_big_m_vergleich.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Für <span class="math inline">y \in \{0,1\}</span> sind beide Modelle gleichwertig; die beiden blauen Punkte stimmen überein. Verschieden ist nur, was dazwischen erlaubt ist — und rechts genügt bereits <span class="math inline">y = 0{,}08</span>, um die volle Kapazität freizugeben.</p>
<h3 id="muster-1-fixkosten-aktivierungsschalter">Muster 1 — Fixkosten / Aktivierungsschalter</h3>

View file

@ -923,8 +923,8 @@ aus.
<hr />
<h2 id="sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform">22.5 Architektur einer produktionsreifen OR-Plattform</h2>
<figure>
<img src="bilder_04/kap15_or_plattform.svg" alt="Abb. 22.2: Architektur einer produktionsreifen OR-Plattform" />
<figcaption aria-hidden="true">Abb. 22.2: Architektur einer produktionsreifen OR-Plattform</figcaption>
<img src="bilder_04/kap15_or_plattform.svg" alt="Abb. 22.2: Die Schichten dieses Abschnitts als Bild — erzeugt aus der Tabelle daneben, damit beide nicht auseinanderlaufen können. Erzeugt von bilder_04/erzeuge_or_architektur.py." />
<figcaption aria-hidden="true">Abb. 22.2: Die Schichten dieses Abschnitts als Bild — erzeugt <strong>aus</strong> der Tabelle daneben, damit beide nicht auseinanderlaufen können. Erzeugt von <code>bilder_04/erzeuge_or_architektur.py</code>.</figcaption>
</figure>
<p>In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein <strong>versionierter, zustandsloser Dienst</strong>:</p>
<table>
@ -973,6 +973,7 @@ aus.
</tr>
</tbody>
</table>
<p><strong>Was Sie in der Abbildung sehen.</strong> Fünf der sechs Schichten bilden einen Weg: Daten gehen oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut sie am Ende auch so: als etwas, das man hinterher noch macht.</p>
<p><strong>Fünf Prinzipien, die sich in der Praxis bewährt haben:</strong></p>
<ol type="1">
<li><strong>Snapshot-Prinzip.</strong> Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.</li>

View file

@ -2104,6 +2104,11 @@ $$
> **🎯 Merksatz** Konvex heißt: Wer bergab geht, kommt am tiefsten Punkt an. Nicht-konvex heißt: Wer bergab geht, kommt *irgendwo* an — und weiß nicht, ob es der tiefste Punkt war.
![Abb. 2.3: Die Funktion $f(x) = x^2 + 3\sin(3x)$, links eindimensional mit den fünf Startpunkten aus
`Konvexitaet_Demo.py`, rechts als separable Fortsetzung $f(x_1,x_2) = f(x_1) + f(x_2)$ — auf jeder Achse liegt genau die Kurve von links. Erzeugt von `bilder_04/erzeuge_3d_konvexitaet.py`.](bilder_04/kap02_konvexitaet_landschaft.svg)
**Was Sie in der Abbildung sehen.** Links sind es fünf Läufe desselben Verfahrens, die in vier verschiedenen Tälern enden — dieselben vier, die Teil 2 von `Konvexitaet_Demo.py` weiter unten auflistet. In zwei Dimensionen werden aus den Tälern Mulden in einem Gitter, und der Unterschied zwischen Start A und Start B beträgt hier bereits mehr als 24 Einheiten. Keiner der beiden Läufe hat etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.
### Was ist konvex, was nicht?
| Problemtyp | Konvex? | Konsequenz |
@ -2423,7 +2428,7 @@ Bis hierher war die lineare Algebra exakt. Ein Rechner rechnet aber nicht exakt,
> **Wenn meine Eingabedaten leicht ungenau sind — wie ungenau ist dann meine Lösung?**
![Abb. 2.3: Was $\kappa(\mathbf{A})$ praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg
![Abb. 2.4: Was $\kappa(\mathbf{A})$ praktisch bedeutet. Links: Über zwölf Größenordnungen hinweg
liegt die gemessene größte Fehlerverstärkung genau auf der theoretischen Schranke — $\kappa$ ist keine grobe Faustregel. Rechts: Was die Ruiz-Equilibrierung an einem Modell mit unverträglichen Einheiten ausrichtet. Erzeugt von `bilder_04/erzeuge_kondition.py`, gerechnet mit derselben Instanz wie `Skalierung_Kondition.py`.](bilder_04/kap_fundament_kondition.svg)
Die Antwort trägt einen Namen: die **Konditionszahl**`\index{Konditionszahl}`{=latex} $\kappa(\mathbf{A})$. Sie ist der wichtigste Begriff dieses Kapitels für den Produktivbetrieb, weil er erklärt, warum ein mathematisch korrektes Modell trotzdem unbrauchbare Ergebnisse liefern kann — oder sich mit einem sachlich falschen `INFEASIBLE` verabschiedet.
@ -4873,7 +4878,9 @@ $$
Der von George `\index{Dantzig, George}`{=latex}Dantzig 1947 formulierte `\index{Simplex-Algorithmus}`{=latex}Simplex-Algorithmus nutzt den Fundamentalsatz aus [Kapitel 2](#kap-fundament): **Das Optimum liegt in einer Ecke.** Statt alle Ecken aufzuzählen, wandert er von Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
![Abb. 5.1: Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über
(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von `bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der Zielfunktion, der Pfeil ihr Gradient $\mathbf{c}$. Erzeugt von `bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
**Was Sie in der Abbildung sehen.** Durch jede besuchte Ecke läuft genau eine Niveaulinie — die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken. Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
### Der Ablauf
@ -5187,6 +5194,11 @@ Zu **jedem** linearen Optimierungsproblem (dem **primalen Problem**`\index{Prima
Prüfen wir das an unserem Beispiel: $\mathbf{b}^\top\mathbf{y}^* = 40 \cdot 12{,}5 + 60 \cdot 31{,}25 + 8 \cdot 0 = 500 + 1875 + 0 = 2375$ ✓ — identisch mit $Z^* = 2375$.
![Abb. 5.3: Beide Probleme geometrisch, gerechnet mit `Dualitaet_Nachweis.py`. Rechts ist die Ebene
$y_3 = 0$ gezeichnet — zulässig, weil das Marktlimit $x_1 \le 8$ bei $x_1^* = 7{,}5$ nicht bindet und deshalb $y_3^* = 0$ gilt. Erzeugt von `bilder_04/erzeuge_dualitaets_bruecke.py`.](bilder_04/kap_lp_dualitaets_bruecke.svg)
**Was Sie in der Abbildung sehen.** Die beiden Tafeln haben nichts gemeinsam: verschiedene Achsen, verschiedene Einheiten, links ein geschlossenes Polyeder, rechts ein nach oben offener Bereich. Links wird maximiert, rechts minimiert. Und doch steht an beiden Optima dieselbe Zahl — 2.375 €. Genau das behauptet der Satz, und mehr ist an ihm nicht zu sehen.
Das lässt sich auch unabhängig von der Handrechnung nachweisen: Primal- und Dualproblem werden als **zwei getrennte, voneinander unabhängige LPs** aufgestellt und gelöst — wenn der starke Dualitätssatz`\index{Starker Dualitätssatz}`{=latex} stimmt, müssen beide denselben Zielwert liefern, ohne dass eines vom anderen „weiß“.
```python
@ -5555,7 +5567,7 @@ Der Zukauf für 18 €/h ist hochprofitabel: Jede zusätzliche Prüfstunde bring
Der vorige Abschnitt hat gezeigt, wie wertvoll Schattenpreise sind. Dieser zeigt die zwei Fälle, in denen sie eine Entscheidung **nicht** tragen — und wie man beide erkennt, bevor jemand auf ihrer Grundlage eine Maschine kauft.
![Abb. 5.3: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen
![Abb. 5.4: Links laufen drei Geraden durch dieselbe Ecke — eine mehr, als zwei Dimensionen
brauchen. Rechts die Folge: Der Schattenpreis ist keine Zahl mehr, sondern eine Spanne. Erzeugt von `bilder_04/erzeuge_entartung_polyeder.py`.](bilder_04/kap_lp_entartung.svg)
**Was Sie in der Abbildung sehen.** Jeder Wert innerhalb der farbigen Balken erfüllt die Optimalitätsbedingungen. Zwei Solver dürfen also verschiedene Schattenpreise melden und haben beide recht — die Frage „was ist eine Stunde wert?" hat hier schlicht keine eindeutige Antwort.
@ -6222,6 +6234,11 @@ Skript löst die LP-Relaxationen und kappt nach denselben drei Regeln, die oben
* seine LP-Schranke schlechter ist als die beste bereits gefundene ganzzahlige Lösung (der **Incumbent**`\index{Branch-and-Bound!Incumbent}`{=latex}),
* oder die LP-Lösung bereits ganzzahlig ist (dann ist dieser Ast fertig).
![Abb. 6.3: Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die
13 ganzzahligen zulässigen Punkte. Erzeugt von `bilder_04/erzeuge_bnb_geometrie.py`.](bilder_04/kap_milp_bnb_geometrie.svg)
**Was Sie in der Abbildung sehen.** Der rote Streifen ist genau das, was Phase 2 wegnimmt — und in ihm liegt kein einziger grauer Punkt. Deshalb ist Verzweigen verlustfrei. Weggenommen wird trotzdem etwas Wertvolles: das LP-Optimum (3; 1,5) selbst. Genau darum sinkt die Schranke von 21 auf 20,67, und genau dieses Sinken ist der Fortschritt, den der Suchbaum Knoten für Knoten protokolliert.
Zusätzlich fügen Solver **Schnittebenen**`\index{Schnittebenen}`{=latex} (*cutting planes*, z. B. Gomory-Cuts`\index{Schnittebenen!Gomory-Cuts}`{=latex}) hinzu: gültige Ungleichungen, die gebrochene Bereiche wegschneiden, ohne einen einzigen zulässigen ganzzahligen Punkt zu entfernen.
> **✏️ Handrechnung 6.2: Ein Suchbaum von Hand**
@ -6246,7 +6263,7 @@ Zusätzlich fügen Solver **Schnittebenen**`\index{Schnittebenen}`{=latex} (*cut
Hier kommt das Handwerkszeug, das MILP so mächtig macht:`\index{Big-M-Methode}`{=latex} Man übersetzt Geschäftsregeln in lineare Ungleichungen — mithilfe binärer Hilfsvariablen $y \in \{0,1\}$ und einer hinreichend großen Konstante $M$.
![Abb. 6.3: Dieselbe Bedingung $x \le M\,y$, zwei Zahlen für $M$. Gezeigt ist die LP-Relaxation —
![Abb. 6.4: Dieselbe Bedingung $x \le M\,y$, zwei Zahlen für $M$. Gezeigt ist die LP-Relaxation —
also das, was der Solver sieht, bevor $y$ ganzzahlig wird. Erzeugt von `bilder_04/erzeuge_big_m_vergleich.py`.](bilder_04/kap_milp_big_m.svg)
**Was Sie in der Abbildung sehen.** Für $y \in \{0,1\}$ sind beide Modelle gleichwertig; die beiden blauen Punkte stimmen überein. Verschieden ist nur, was dazwischen erlaubt ist — und rechts genügt bereits $y = 0{,}08$, um die volle Kapazität freizugeben.
@ -7693,6 +7710,11 @@ Der **CP-SAT-Solver**`\index{CP-SAT-Solver}`{=latex} von Google OR-Tools verbind
>
> **Und wenn ein Widerspruch auftritt?** Dann lernt der SAT-Kern eine **Sperrklausel** (*nogood*): „Diese Kombination nie wieder probieren.“ Der Solver springt dann nicht nur einen Schritt zurück, sondern direkt zur Ursache des Konflikts (*non-chronological backtracking*).
![Abb. 7.2: Dieselbe Handrechnung als Bild: grün die Werte, die in der Domain bleiben, rot der Wert,
den die jeweilige Runde streicht, grau die schon vorher gestrichenen. Erzeugt von `bilder_04/erzeuge_propagation_ablauf.py`.](bilder_04/kap_cpsat_propagation_ablauf.svg)
**Was Sie in der Abbildung sehen.** Jede Runde streicht genau **einen** Wert — und trotzdem sinkt die Zahl der Kombinationen von 16 auf 9, also um gut vierzig Prozent. Das ist der Hebel: Gestrichen wird nicht eine Kombination, sondern ein Wert, und mit ihm alle Kombinationen, in denen er vorkam.
```python
#!/usr/bin/env python3
@ -8273,11 +8295,14 @@ Erreichter Makespan: 11
==========================================================================
```
![Abb. 7.2: Job-Shop-Plan als Gantt-Diagramm](bilder_04/kap06_jobshop_gantt.svg)
![Abb. 7.3: Derselbe Plan als Gantt-Diagramm. Schraffiert die beiden Stillstände zwischen zwei
Arbeitsgängen, gestrichelt die untere Schranke. Erzeugt von `bilder_04/erzeuge_kap06_gantt.py`.](bilder_04/kap06_jobshop_gantt.svg)
*Eine interaktive Fassung dieser Grafik steht auf den Kapitelseiten der Website bereit.*
**Lesen Sie das Gantt-Diagramm.** Auftrag 1 kommt zuerst an die Fräse (02), Auftrag 0 muss warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig ist die Lücke in der Dreherei bei Zeitpunkt 4: Die Maschine steht still, obwohl Auftrag 0 schon Arbeit hätte — er ist aber bis 5 noch an der Fräse. Dieses **Warten ist unvermeidlich**; es entsteht aus dem Zusammenspiel von Reihenfolge- und Maschinenbedingungen, nicht aus einer schlechten Planung.
**Lesen Sie das Gantt-Diagramm.** Auftrag 1 kommt zuerst an die Fräse (02), Auftrag 0 muss warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig sind die beiden schraffierten Lücken. Die in der Dreherei bei Zeitpunkt 4 ist die sprechendere: Die Maschine steht still, obwohl Auftrag 0 schon Arbeit hätte — er ist aber bis 5 noch an der Fräse. Dieses **Warten ist unvermeidlich**; es entsteht aus dem Zusammenspiel von Reihenfolge- und Maschinenbedingungen, nicht aus einer schlechten Planung.
Die gestrichelte Linie bei 10 sagt dasselbe von der anderen Seite. Sie ist die untere Schranke aus dem Programm, und sie stammt von der Dreherei: Deren drei Arbeitsgänge dauern zusammen 2 + 4 + 4 = 10 Zeiteinheiten, die sich nicht überlappen dürfen. Die Dreherei ist im Plan von 0 bis 11 belegt — also genau ihre 10 Einheiten Arbeit plus die eine schraffierte Einheit Stillstand. Der Plan ist somit nicht deshalb elf lang, weil schlecht geplant wurde, sondern weil die Engpassmaschine einmal warten muss. (Die zweite Lücke, in der Lackiererei, kostet nichts: Diese Maschine ist ohnehin nicht der Engpass.)
> **⚠️ Mehrere gleich gute Pläne — und was das für Ihre Tests bedeutet**
>
@ -15778,7 +15803,7 @@ dem CO2-Preis, den das Unternehmen ohnehin ansetzt.
Die Front hat **zehn** Punkte. Ein Raster aus 1 401 Gewichten findet **sechs** davon. Vier Kompromisse sind für kein Gewicht erreichbar — und das ist keine Frage der Rasterweite.
![Abb. 14.1: Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen
Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt.](bilder_04/kap_mehrziel_pareto.svg)
Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal, werden aber von keiner Geraden gestützt. Die dünne Gerade ist eine solche Stützgerade, die punktierte die parallele Gerade durch den am weitesten abliegenden der vier Punkte. Erzeugt von `bilder_04/erzeuge_pareto_front.py`.](bilder_04/kap_mehrziel_pareto.svg)
*Eine interaktive Fassung dieser Grafik steht auf den Kapitelseiten der Website bereit.*
@ -15786,6 +15811,8 @@ Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-o
Das Programm rechnet diese Hülle in `untere_huelle()` unabhängig aus und vergleicht: Genau die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe steht in der Ausgabe.
**Was Sie in der Abbildung sehen.** Die beiden Geraden haben dieselbe Steigung, also dasselbe Gewicht $w$. 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 [Abschnitt 14.7](#sec:mehrziel-epsilon) geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts findet genau diesen Punkt.
> **⚠️ Der Grund liegt in der Ganzzahligkeit**
>
> 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 **ganzzahlig** werden (welcher Träger, welches Lager, welche Schicht), zerfällt der Bereich in einzelne Punkte, und zwischen ihnen entstehen Einbuchtungen.
@ -19842,6 +19869,11 @@ CVaR: Portfolio 101.33 vs. Summe 159.24 -> subadditiv (kohaerent)
Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand nachrechnen können: Einzeln meldet er für jede Anleihe einen *Gewinn* von 2 €, im Portfolio einen *Verlust* von 98 €. Diversifikation hätte demnach das Risiko um 102 € erhöht. Der CVaR dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter Betrachtung — die Streuung **senkt** das Risiko, wie es sein muss.
![Abb. 20.1: Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie `VaR_CVaR_Demo.py`. Erzeugt
von `bilder_04/erzeuge_var_vs_cvar.py`.](bilder_04/kap_cvar_subadditivitaet.svg)
**Was Sie in der Abbildung sehen.** Links wechselt der Balken über null das Vorzeichen: Aus zwei gemeldeten Gewinnen wird im Portfolio ein Verlust. Rechts passiert nichts dergleichen — der Portfoliobalken ist kürzer als der Summenbalken, und genau das heißt Subadditivität. Der Grund für den Sprung links ist keine Rechenungenauigkeit, sondern die Bauart des Maßes: Eine Schwelle bei 95 % sieht ein Ereignis mit 4 % Wahrscheinlichkeit nicht — bis zwei davon zusammenkommen.
> **⚠️ Eine Falle bei der CVaR-Berechnung** Der naheliegende Weg — „Mittelwert aller Verluste $\ge$ VaR“ — ist **falsch**, sobald die Verteilung **Atome** hat (also einzelne Werte mit positiver Wahrscheinlichkeit, wie hier die zwei möglichen Ausgänge). Der VaR liegt dann selbst auf einem Atom, und der Vergleich `>=` erfasst zu viel Wahrscheinlichkeitsmasse. Im Beispiel oben liefert dieser naive Schätzer für Anleihe A den Wert 2,08 statt der korrekten 79,59 — ein Fehler um Faktor 38.
>
> Die Rockafellar-Uryasev-Formel behandelt Atome von sich aus korrekt. Verwenden Sie sie auch dann, wenn Sie „nur schnell“ einen CVaR ausrechnen wollen.
@ -19850,7 +19882,7 @@ Und Teil 2 zeigt das Grundproblem des VaR an einem Beispiel, das Sie von Hand na
## 20.4 Value at Risk und Conditional Value at Risk {#sec:cvar-value-at-risk-und-conditional-value}
![Abb. 20.1: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus
![Abb. 20.2: Der VaR ist eine Schwelle, der CVaR ein Mittelwert. 20 000 simulierte Tagesrenditen aus
dem Fat-Tail-Modell dieses Kapitels ($t$-Verteilung mit drei Freiheitsgraden). Der schraffierte Bereich sind die schlechtesten 5 % der Tage; der VaR markiert nur ihren Rand, der CVaR ihren Mittelwert. Erzeugt von `bilder_04/erzeuge_var_cvar.py`.](bilder_04/kap_cvar_var_vergleich.svg)
**Value at Risk ($\text{VaR}_\alpha$):**`\index{Value at Risk (VaR)}`{=latex} Der Verlust, der mit Wahrscheinlichkeit $\alpha$ nicht überschritten wird.
@ -19925,7 +19957,7 @@ $$
= \lVert \mathbf{w}-\mathbf{w}_{\text{alt}}\rVert_1
$$
![Abb. 20.2: CVaR-Zielfunktion mit Reibungs-Penalty](bilder_04/kap13_cvar_zielfunktion.svg)
![Abb. 20.3: CVaR-Zielfunktion mit Reibungs-Penalty](bilder_04/kap13_cvar_zielfunktion.svg)
> **📐 Formel-Lesehilfe** Die $L_1$-Norm ist die Summe der **Beträge**. Sie misst, wie viel Prozent des Portfolios insgesamt bewegt werden — Käufe und Verkäufe zusammen.
>
@ -22050,7 +22082,8 @@ Alle drei Antworten stehen bis hierhin in Tabellen. Tabellen liest niemand in ei
## 22.5 Architektur einer produktionsreifen OR-Plattform {#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
![Abb. 22.2: Architektur einer produktionsreifen OR-Plattform](bilder_04/kap15_or_plattform.svg)
![Abb. 22.2: Die Schichten dieses Abschnitts als Bild — erzeugt **aus** der Tabelle daneben, damit beide
nicht auseinanderlaufen können. Erzeugt von `bilder_04/erzeuge_or_architektur.py`.](bilder_04/kap15_or_plattform.svg)
In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein **versionierter, zustandsloser Dienst**:
@ -22063,6 +22096,8 @@ In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein **
| **Ausführung** | Kalender-Sync, Benachrichtigung, Order-Routing | Kalender-APIs, Messaging, Broker |
| **Betrieb** | Monitoring, Audit-Trail, Alarme | Logging, Metriken, Versionierung |
**Was Sie in der Abbildung sehen.** Fünf der sechs Schichten bilden einen Weg: Daten gehen oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut sie am Ende auch so: als etwas, das man hinterher noch macht.
**Fünf Prinzipien, die sich in der Praxis bewährt haben:**
1. **Snapshot-Prinzip.**`\index{Snapshot-Prinzip}`{=latex} Jeder Optimierungslauf arbeitet auf einem unveränderlichen Datenschnappschuss mit eigener ID. Nur so ist ein Ergebnis später reproduzierbar — und im Streitfall belegbar.

View file

@ -457,6 +457,18 @@ und deren Zahl wächst wieder kombinatorisch.
> Konvex heißt: Wer bergab geht, kommt am tiefsten Punkt an. Nicht-konvex heißt: Wer bergab
> geht, kommt *irgendwo* an — und weiß nicht, ob es der tiefste Punkt war.
![Die Funktion $f(x) = x^2 + 3\sin(3x)$, links eindimensional mit den fünf Startpunkten aus
`Konvexitaet_Demo.py`, rechts als separable Fortsetzung
$f(x_1,x_2) = f(x_1) + f(x_2)$ — auf jeder Achse liegt genau die Kurve von links. Erzeugt von
`bilder_04/erzeuge_3d_konvexitaet.py`.](bilder_04/kap02_konvexitaet_landschaft.svg)
**Was Sie in der Abbildung sehen.** Links sind es fünf Läufe desselben Verfahrens, die in
vier verschiedenen Tälern enden — dieselben vier, die Teil 2 von `Konvexitaet_Demo.py`
weiter unten auflistet. In
zwei Dimensionen werden aus den Tälern Mulden in einem Gitter, und der Unterschied zwischen
Start A und Start B beträgt hier bereits mehr als 24 Einheiten. Keiner der beiden Läufe hat
etwas falsch gemacht: Beide enden dort, wo es nicht mehr bergab geht.
### Was ist konvex, was nicht?
| Problemtyp | Konvex? | Konsequenz |

View file

@ -181,9 +181,16 @@ Ecke zu Ecke — und zwar immer in Richtung Verbesserung.
![Derselbe zulässige Bereich, aber der Weg der Handrechnung weiter unten: vom Ursprung über
(0; 8) zum Optimum (7,5; 5). Drei der fünf Ecken werden besucht, keine zweimal — das ist der
ganze Gewinn gegenüber dem Aufzählen aller Ecken. Erzeugt von
ganze Gewinn gegenüber dem Aufzählen aller Ecken. Punktiert die Niveaulinien der
Zielfunktion, der Pfeil ihr Gradient $\mathbf{c}$. Erzeugt von
`bilder_04/erzeuge_polyeder.py`.](bilder_04/kap_lp_simplex_ecken.svg)
**Was Sie in der Abbildung sehen.** Durch jede besuchte Ecke läuft genau eine Niveaulinie —
die Linien sind nicht gewählt, sondern die Zielwerte 0 €, 2.000 € und 2.375 € der drei Ecken.
Der Simplex schiebt diese Linie in Richtung des Pfeils so weit hinaus, wie das Polyeder es
zulässt. Dass er dabei an einer Ecke anhält und nicht mittendrin, ist keine Eigenheit des
Verfahrens, sondern der Fundamentalsatz: Weiter geht es nur noch hinaus.
### Der Ablauf
1. **Initialisierung.** Starte an einer bekannten Ecke — meist dem Ursprung
@ -540,6 +547,16 @@ Prüfen wir das an unserem Beispiel: $\mathbf{b}^\top\mathbf{y}^*
= 40 \cdot 12{,}5 + 60 \cdot 31{,}25 + 8 \cdot 0 = 500 + 1875 + 0 = 2375$ ✓ — identisch mit
$Z^* = 2375$.
![Beide Probleme geometrisch, gerechnet mit `Dualitaet_Nachweis.py`. Rechts ist die Ebene
$y_3 = 0$ gezeichnet — zulässig, weil das Marktlimit $x_1 \le 8$ bei $x_1^* = 7{,}5$ nicht
bindet und deshalb $y_3^* = 0$ gilt. Erzeugt von
`bilder_04/erzeuge_dualitaets_bruecke.py`.](bilder_04/kap_lp_dualitaets_bruecke.svg)
**Was Sie in der Abbildung sehen.** Die beiden Tafeln haben nichts gemeinsam: verschiedene
Achsen, verschiedene Einheiten, links ein geschlossenes Polyeder, rechts ein nach oben
offener Bereich. Links wird maximiert, rechts minimiert. Und doch steht an beiden Optima
dieselbe Zahl — 2.375 €. Genau das behauptet der Satz, und mehr ist an ihm nicht zu sehen.
Das lässt sich auch unabhängig von der Handrechnung nachweisen: Primal- und Dualproblem
werden als **zwei getrennte, voneinander unabhängige LPs** aufgestellt und gelöst — wenn
der starke Dualitätssatz{idx:Starker Dualitätssatz} stimmt, müssen beide denselben Zielwert liefern, ohne dass eines

View file

@ -328,6 +328,16 @@ wird verworfen, weil sein Wert 18 den Incumbent 20 aus Ast A nicht schlägt. Erz
(der **Incumbent**{idx:Branch-and-Bound!Incumbent}),
* oder die LP-Lösung bereits ganzzahlig ist (dann ist dieser Ast fertig).
![Was der erste Verzweigungsschritt des Suchbaums geometrisch bedeutet. Graue Punkte sind die
13 ganzzahligen zulässigen Punkte. Erzeugt von
`bilder_04/erzeuge_bnb_geometrie.py`.](bilder_04/kap_milp_bnb_geometrie.svg)
**Was Sie in der Abbildung sehen.** Der rote Streifen ist genau das, was Phase 2 wegnimmt —
und in ihm liegt kein einziger grauer Punkt. Deshalb ist Verzweigen verlustfrei. Weggenommen
wird trotzdem etwas Wertvolles: das LP-Optimum (3; 1,5) selbst. Genau darum sinkt die
Schranke von 21 auf 20,67, und genau dieses Sinken ist der Fortschritt, den der Suchbaum
Knoten für Knoten protokolliert.
Zusätzlich fügen Solver **Schnittebenen**{idx:Schnittebenen} (*cutting planes*, z. B. Gomory-Cuts{idx:Schnittebenen!Gomory-Cuts}) hinzu:
gültige Ungleichungen, die gebrochene Bereiche wegschneiden, ohne einen einzigen zulässigen
ganzzahligen Punkt zu entfernen.

View file

@ -157,6 +157,15 @@ Projektterminierung und Job-Shop-Scheduling.
> einen Schritt zurück, sondern direkt zur Ursache des Konflikts
> (*non-chronological backtracking*).
![Dieselbe Handrechnung als Bild: grün die Werte, die in der Domain bleiben, rot der Wert,
den die jeweilige Runde streicht, grau die schon vorher gestrichenen. Erzeugt von
`bilder_04/erzeuge_propagation_ablauf.py`.](bilder_04/kap_cpsat_propagation_ablauf.svg)
**Was Sie in der Abbildung sehen.** Jede Runde streicht genau **einen** Wert — und trotzdem
sinkt die Zahl der Kombinationen von 16 auf 9, also um gut vierzig Prozent. Das ist der
Hebel: Gestrichen wird nicht eine Kombination, sondern ein Wert, und mit ihm alle
Kombinationen, in denen er vorkam.
```python
#!/usr/bin/env python3
@ -776,15 +785,26 @@ Erreichter Makespan: 11
==========================================================================
```
![Job-Shop-Plan als Gantt-Diagramm](bilder_04/kap06_jobshop_gantt.svg)
![Derselbe Plan als Gantt-Diagramm. Schraffiert die beiden Stillstände zwischen zwei
Arbeitsgängen, gestrichelt die untere Schranke. Erzeugt von
`bilder_04/erzeuge_kap06_gantt.py`.](bilder_04/kap06_jobshop_gantt.svg)
{plotly:kap06_jobshop_gantt}
**Lesen Sie das Gantt-Diagramm.** Auftrag 1 kommt zuerst an die Fräse (02), Auftrag 0 muss
warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig ist die Lücke in der Dreherei
bei Zeitpunkt 4: Die Maschine steht still, obwohl Auftrag 0 schon Arbeit hätte — er ist aber
bis 5 noch an der Fräse. Dieses **Warten ist unvermeidlich**; es entsteht aus dem
Zusammenspiel von Reihenfolge- und Maschinenbedingungen, nicht aus einer schlechten Planung.
warten und ist dort erst von 2 bis 5 an der Reihe. Auffällig sind die beiden schraffierten
Lücken. Die in der Dreherei bei Zeitpunkt 4 ist die sprechendere: Die Maschine steht still,
obwohl Auftrag 0 schon Arbeit hätte — er ist aber bis 5 noch an der Fräse. Dieses **Warten
ist unvermeidlich**; es entsteht aus dem Zusammenspiel von Reihenfolge- und
Maschinenbedingungen, nicht aus einer schlechten Planung.
Die gestrichelte Linie bei 10 sagt dasselbe von der anderen Seite. Sie ist die untere Schranke
aus dem Programm, und sie stammt von der Dreherei: Deren drei Arbeitsgänge dauern zusammen
2 + 4 + 4 = 10 Zeiteinheiten, die sich nicht überlappen dürfen. Die Dreherei ist im Plan von 0
bis 11 belegt — also genau ihre 10 Einheiten Arbeit plus die eine schraffierte Einheit
Stillstand. Der Plan ist somit nicht deshalb elf lang, weil schlecht geplant wurde, sondern
weil die Engpassmaschine einmal warten muss. (Die zweite Lücke, in der Lackiererei, kostet
nichts: Diese Maschine ist ohnehin nicht der Engpass.)
> **⚠️ Mehrere gleich gute Pläne — und was das für Ihre Tests bedeutet**
>

View file

@ -543,7 +543,9 @@ Kompromisse sind für kein Gewicht erreichbar — und das ist keine Frage der Ra
![Pareto-Front: Kosten gegen CO₂. Die gefüllten Punkte liegen auf der unteren konvexen
Hülle und sind über Gewichte erreichbar; die vier offenen Kreise sind pareto-optimal,
werden aber von keiner Geraden gestützt.](bilder_04/kap_mehrziel_pareto.svg)
werden aber von keiner Geraden gestützt. Die dünne Gerade ist eine solche Stützgerade, die
punktierte die parallele Gerade durch den am weitesten abliegenden der vier Punkte. Erzeugt
von `bilder_04/erzeuge_pareto_front.py`.](bilder_04/kap_mehrziel_pareto.svg)
{plotly:kap_mehrziel_pareto}
@ -557,6 +559,15 @@ Das Programm rechnet diese Hülle in `untere_huelle()` unabhängig aus und vergl
die vier Punkte, die das Gewichtsraster verfehlt, liegen strikt oberhalb. Die Gegenprobe
steht in der Ausgabe.
**Was Sie in der Abbildung sehen.** Die beiden Geraden haben dieselbe Steigung, also dasselbe
Gewicht $w$. 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
{ref:sec:mehrziel-epsilon} geht: Eine Schranke für den CO₂-Ausstoß statt eines Gewichts
findet genau diesen Punkt.
> **⚠️ Der Grund liegt in der Ganzzahligkeit**
>
> Bei einem reinen LP ist der zulässige Bereich konvex, und die Pareto-Front liegt vollständig

View file

@ -254,6 +254,16 @@ können: Einzeln meldet er für jede Anleihe einen *Gewinn* von 2 €, im Portfo
dagegen verhält sich korrekt: 101,33 € im Portfolio gegenüber 159,24 € bei getrennter
Betrachtung — die Streuung **senkt** das Risiko, wie es sein muss.
![Dieselbe Tabelle als Bild, gerechnet mit denselben Ziehungen wie `VaR_CVaR_Demo.py`. Erzeugt
von `bilder_04/erzeuge_var_vs_cvar.py`.](bilder_04/kap_cvar_subadditivitaet.svg)
**Was Sie in der Abbildung sehen.** Links wechselt der Balken über null das Vorzeichen: Aus
zwei gemeldeten Gewinnen wird im Portfolio ein Verlust. Rechts passiert nichts dergleichen —
der Portfoliobalken ist kürzer als der Summenbalken, und genau das heißt Subadditivität. Der
Grund für den Sprung links ist keine Rechenungenauigkeit, sondern die Bauart des Maßes: Eine
Schwelle bei 95 % sieht ein Ereignis mit 4 % Wahrscheinlichkeit nicht — bis zwei davon
zusammenkommen.
> **⚠️ Eine Falle bei der CVaR-Berechnung**
> Der naheliegende Weg — „Mittelwert aller Verluste $\ge$ VaR“ — ist **falsch**, sobald die
> Verteilung **Atome** hat (also einzelne Werte mit positiver Wahrscheinlichkeit, wie hier

View file

@ -918,7 +918,9 @@ und das ist weniger trivial, als es aussieht:
## Architektur einer produktionsreifen OR-Plattform {#sec:praxisfallen-architektur-einer-produktionsreifen-or-plattform}
![Architektur einer produktionsreifen OR-Plattform](bilder_04/kap15_or_plattform.svg)
![Die Schichten dieses Abschnitts als Bild — erzeugt **aus** der Tabelle daneben, damit beide
nicht auseinanderlaufen können. Erzeugt von
`bilder_04/erzeuge_or_architektur.py`.](bilder_04/kap15_or_plattform.svg)
In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein
**versionierter, zustandsloser Dienst**:
@ -932,6 +934,12 @@ In einer professionellen Umgebung ist der Optimierer kein Skript, sondern ein
| **Ausführung** | Kalender-Sync, Benachrichtigung, Order-Routing | Kalender-APIs, Messaging, Broker |
| **Betrieb** | Monitoring, Audit-Trail, Alarme | Logging, Metriken, Versionierung |
**Was Sie in der Abbildung sehen.** Fünf der sechs Schichten bilden einen Weg: Daten gehen
oben hinein und unten als Handlung wieder hinaus. Die sechste steht daneben, und das ist
keine zeichnerische Verlegenheit — Monitoring und Audit-Trail sind keine Station, die man
durchläuft, sondern eine Anforderung an jede. Wer sie als sechsten Kasten unten anhängt, baut
sie am Ende auch so: als etwas, das man hinterher noch macht.
**Fünf Prinzipien, die sich in der Praxis bewährt haben:**
1. **Snapshot-Prinzip.**{idx:Snapshot-Prinzip} Jeder Optimierungslauf arbeitet auf einem unveränderlichen

View file

@ -2539,6 +2539,59 @@ Neu: `bilder_04/stil_04.py` bündelt Palette, `rcParams` und `speichere()`. `net
entgegen der Planung **nicht** gebraucht — `pyproject.toml` bleibt unverändert.
PDF von 781 auf **788 Seiten**, 44 Diagramme, 27 Generatoren.
### ✅ Grafik-Upgrade, Stufe 2: die neun Überarbeitungen
Erledigt: Konvexität, Simplex-Pfad, Primal/Dual, Branch-and-Bound, Propagation, Gantt,
Pareto, VaR/CVaR, OR-Architektur.
**Der Befund, der die Arbeit geprägt hat: „Überarbeiten" hieß fast nie „ersetzen".** Acht der
neun vorhandenen Diagramme zeigten etwas anderes als das, was der Auftrag verlangte — die
Sehnenbedingung neben der Talkarte, den Suchbaum neben der Polyedergeometrie, die
Verlustverteilung neben der Subadditivität. Sie zu überschreiben hätte Inhalt vernichtet.
Also sind sechs neue Bilder danebengetreten, und nur an drei Stellen wurde ein vorhandener
Generator erweitert. Beim ersten Bild hatte ich `kap02_konvexitaet.svg` bereits überschrieben,
bevor der Auftragstext („**Ergänzung** zu Abb. 2.2") das widerlegte — zurückgenommen mit
`git checkout`.
**Der einzige echte Ersatz war zugleich ein Fund:** `kap15_or_plattform.svg` war
handgezeichnet und zeigte **fünf** Schichten, während die Tabelle unmittelbar daneben
**sechs** nennt — „Betrieb" (Monitoring, Audit-Trail, Alarme) fehlte, „Audit-Trail" kam nur
noch als Pfeilbeschriftung vor. `erzeuge_or_architektur.py` liest die Schichten jetzt **aus
dieser Tabelle** und bricht ab, wenn deren Aufbau sich ändert. Damit ist das Kriterium aus
6c geschärft: Schematisch heißt nur dann „kein Generator nötig", wenn das Bild keine
Aufzählung und keine Zahl aus dem Text wiederholt.
**Vier eigene Fehler, die die Selbsttests gefunden haben:**
* Die Prüfung der Pareto-Tangente stand **verkehrt herum** — bei einer *unteren* konvexen
Hülle liegen alle Punkte **über** der Stützgeraden, nicht darunter. Der Test schlug an,
bevor das Bild jemanden in die Irre führen konnte.
* `erzeuge_or_architektur.py` trug **fünf** beschriftete Übergänge für **vier** Pfeile; die
fünfte („Freigabe") wurde still nicht gezeichnet. Meine erste Zählprüfung war um eins
daneben und ließ es durch.
* Wieder `.replace(",", ".")` auf einen **mehrteiligen** f-String — aus `Delle: (13978, 5912)`
wurde `(13978. 5912)`. Derselbe Fehler wie mehrfach zuvor; die Zahl wird jetzt getrennt
formatiert.
* Im Gantt-Text hatte ich behauptet, die eine Zeiteinheit zwischen Schranke 10 und Makespan 11
stecke „in den beiden schraffierten Feldern". Sie steckt allein in der Dreherei — der
Engpassmaschine. Der Generator rechnet die Summe jetzt nach: 10 Arbeit + 1 Stillstand = 11.
**Nachweise:** Alle 53 Bilddateien sind über zwei Läufe byte-identisch. Die drei erweiterten
Generatoren haben ihre *anderen* Ausgaben nicht verändert — `kap_fundament_polyeder.svg` und
beide Plotly-Fassungen sind bitgleich geblieben. Zahlen gegen das Buch geprüft: die fünf
BFGS-Läufe der Konvexität ziffergenau, $Z^*=W^*=2375$ bei der Dualität, 16 → 12 → 9 bei der
Propagation, 21 → 20,67 bei Branch-and-Bound, und beim CVaR alle vier Zeilen einschließlich
der Unterscheidung 79,59 gegen 79,66 — was beweist, dass die Ziehungsreihenfolge des
Buchprogramms getroffen ist.
PDF von 788 auf **792 Seiten**, 48 Kapiteldiagramme (38 erzeugt, 10 schematisch),
33 Generatoren, davon 17 auf `stil_04.py`.
**Offen, weil es eine Entscheidung des Autors ist:** Die Bildunterschriften tragen im PDF
eine **doppelte Nummer** — „Abbildung 21: Abb. 7.2: …". Die erste vergibt LaTeX, die zweite
`nummeriere_marken()`. Das betrifft alle 48 Abbildungen und besteht seit Langem; behoben wäre
es dadurch, dass die LaTeX-Nummerierung der Gleitumgebung unterdrückt wird.
---
## 8. Commit-Historie des V04-Strangs

View file

@ -0,0 +1,195 @@
#!/usr/bin/env python3
# erzeuge_3d_konvexitaet.py
"""
Erzeugt die Landschaftsgrafik zum Abschnitt Konvexitaet:
bilder_04/kap02_konvexitaet_landschaft.svg
ERGAENZT das vorhandene kap02_konvexitaet.svg, ersetzt es nicht: Jenes zeigt die
Sehnenbedingung - die Definition. Dieses zeigt ihre Folge - dass bei einer
nicht-konvexen Funktion der Startpunkt darueber entscheidet, was am Ende
herauskommt.
Beide Tafeln zeigen DIESELBE Funktion wie das Kapitelprogramm
Konvexitaet_Demo.py:
links f(x) = x^2 + 3 sin(3x) - eindimensional
rechts f(x1, x2) = x1^2 + 3 sin(3 x1) + x2^2 + 3 sin(3 x2) - separabel
Die rechte Funktion ist die separable Fortsetzung der linken: Auf jeder Achse
liegt exakt die Kurve der linken Tafel. Deshalb ist die rechte Tafel keine neue
Behauptung, sondern dieselbe eine Dimension weiter - und die Taeler, die links
nebeneinander liegen, werden rechts zu einem Gitter aus Mulden.
Gerechnet wird mit BFGS aus denselben Startpunkten wie im Buchprogramm. Die
fuenf Ergebnisse der linken Tafel muessen deshalb Ziffer fuer Ziffer der im
Kapitel abgedruckten Tabelle entsprechen; pruefe_gegen_buch() bricht ab, wenn
nicht. Ein Bild, das andere Minima zeigt als die Tabelle daneben, waere
schlimmer als gar kein Bild.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_3d_konvexitaet.py
Benoetigt: numpy, scipy, matplotlib
"""
from __future__ import annotations
import os
import sys
import numpy as np
from scipy.optimize import minimize
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
# Die Startpunkte des Buchprogramms - nicht selbst gewaehlt.
STARTPUNKTE = [-3.0, -1.5, 0.0, 1.5, 3.0]
# Die im Kapitel abgedruckte Tabelle: Start -> (Minimum, Funktionswert).
# Nur zum Gegenpruefen; gerechnet wird unabhaengig davon.
TABELLE_IM_BUCH = {
-3.0: (-2.4280, 3.3695),
-1.5: (-0.4874, -2.7448),
0.0: (-0.4874, -2.7448),
1.5: (1.4606, -0.7042),
3.0: (3.3817, 9.4570),
}
def welle(x):
"""Die Funktion des Buchprogramms: Parabel mit aufmodulierter Welle."""
return x ** 2 + 3.0 * np.sin(3.0 * x)
def welle_1d(x):
"""Dieselbe Funktion in der Form, die scipy.optimize erwartet."""
return welle(x[0])
def welle_2d(x):
"""Separable Fortsetzung in zwei Dimensionen."""
return welle(x[0]) + welle(x[1])
def suche_1d():
"""Lokale Suche von jedem Startpunkt - wie in Teil 2 des Buchprogramms."""
ergebnisse = []
for start in STARTPUNKTE:
res = minimize(welle_1d, x0=[start], method="BFGS")
ergebnisse.append((start, float(res.x[0]), float(res.fun)))
return ergebnisse
def suche_2d(start):
"""Dieselbe lokale Suche in 2D, mit aufgezeichnetem Weg."""
weg = [np.array(start, dtype=float)]
minimize(welle_2d, x0=list(start), method="BFGS",
callback=lambda xk: weg.append(np.array(xk, dtype=float)))
return np.array(weg)
def pruefe_gegen_buch(ergebnisse) -> None:
"""Bild und abgedruckte Tabelle muessen dieselben Minima nennen."""
for start, ort, wert in ergebnisse:
soll_ort, soll_wert = TABELLE_IM_BUCH[start]
if abs(ort - soll_ort) > 5e-4 or abs(wert - soll_wert) > 5e-4:
raise SystemExit(
f"Konvexitaets-Landschaft: Start {start:+.1f} fuehrt zu "
f"x = {ort:.4f} (f = {wert:.4f}), das Kapitel druckt "
f"x = {soll_ort:.4f} (f = {soll_wert:.4f}) ab.")
gefunden = len({round(ort, 3) for _, ort, _ in ergebnisse})
if gefunden != 4:
raise SystemExit(
f"Konvexitaets-Landschaft: {gefunden} verschiedene Minima, das "
f"Kapitel druckt 4 ab.")
def zeichne(ergebnisse, wege) -> None:
figur, achsen = plt.subplots(1, 2, figsize=(10.0, 4.3))
# --- links: die Funktion des Buchprogramms mit ihren fuenf Laeufen -----
links = achsen[0]
x = np.linspace(-3.8, 3.8, 800)
links.plot(x, welle(x), "-", color=FARBEN["haupt"], linewidth=2.0,
zorder=3)
bester = min(ergebnisse, key=lambda z: z[2])
for start, ort, wert in ergebnisse:
global_ = abs(ort - bester[1]) < 1e-3
farbe = FARBEN["gut"] if global_ else FARBEN["fehler"]
links.annotate("", xy=(ort, wert), xytext=(start, welle(start)),
arrowprops=dict(arrowstyle="->", color=farbe,
linewidth=1.4,
connectionstyle="arc3,rad=0.18"),
zorder=4)
links.plot(start, welle(start), "o", color=farbe, markersize=6,
zorder=5)
links.plot(ort, wert, "*", color=farbe, markersize=13, zorder=6)
links.set_xlabel("$x$")
links.set_ylabel("$f(x)$")
links.set_title("fünf Startpunkte, vier verschiedene Minima",
fontsize=10.5, color=FARBEN["text"])
links.annotate("grün: im tiefsten Tal gelandet\nrot: in einem anderen",
xy=(0.36, 0.97), xycoords="axes fraction", fontsize=8.5,
color=FARBEN["text"], va="top")
# --- rechts: dieselbe Funktion, eine Dimension weiter ------------------
rechts = achsen[1]
gitter = np.linspace(-3.8, 3.8, 420)
xx, yy = np.meshgrid(gitter, gitter)
zz = welle(xx) + welle(yy)
rechts.contourf(xx, yy, zz, levels=28, cmap="Blues_r", alpha=0.7, zorder=1)
rechts.contour(xx, yy, zz, levels=16, colors=[FARBEN["linie"]],
linewidths=0.5, zorder=2)
for name, weg, farbe, versatz, ausrichtung in wege:
rechts.plot(weg[:, 0], weg[:, 1], "-", color=farbe, linewidth=2.2,
zorder=4)
rechts.plot(*weg[0], "o", color=farbe, markersize=7, zorder=5)
rechts.plot(*weg[-1], "*", color=farbe, markersize=15, zorder=6)
rechts.annotate(f"{name}\nf = {welle_2d(weg[-1]):.2f}",
xy=weg[-1], xytext=versatz,
textcoords="offset points", fontsize=8.5,
color=farbe, fontweight="bold", ha=ausrichtung,
zorder=7)
rechts.set_xlabel("$x_1$")
rechts.set_ylabel("$x_2$")
rechts.set_title("dieselbe Funktion in zwei Dimensionen",
fontsize=10.5, color=FARBEN["text"])
rechts.grid(visible=False)
speichere(figur, "kap02_konvexitaet_landschaft")
if __name__ == "__main__":
ergebnisse = suche_1d()
for start, ort, wert in ergebnisse:
print(f" Start {start:>5.1f} -> x = {ort:>8.4f} f = {wert:>8.4f}")
pruefe_gegen_buch(ergebnisse)
print(" stimmt mit der abgedruckten Tabelle überein (5 Zeilen, 4 Minima)")
weg_a = suche_2d((-1.4, -1.6))
weg_b = suche_2d((2.9, 3.1))
if welle_2d(weg_a[-1]) >= welle_2d(weg_b[-1]):
raise SystemExit("Konvexitaets-Landschaft: Lauf A landet nicht "
"tiefer als Lauf B - Farben und Aussage verkehrt.")
if np.linalg.norm(weg_a[-1] - weg_b[-1]) < 1.0:
raise SystemExit("Konvexitaets-Landschaft: Beide 2D-Laeufe enden im "
"selben Tal - dann ist keine Falle zu sehen.")
# Der Begleittext nennt diesen Abstand ("mehr als 24 Einheiten").
abstand = welle_2d(weg_b[-1]) - welle_2d(weg_a[-1])
if not 24.0 <= abstand < 25.0:
raise SystemExit(
f"Konvexitaets-Landschaft: Der Abstand der beiden Laeufe betraegt "
f"{abstand:.2f} Einheiten. Der Text im Kapitel sagt 'mehr als 24' "
f"- beides muss zusammenpassen.")
print(f" 2D: Lauf A endet bei f = {welle_2d(weg_a[-1]):.2f}, "
f"Lauf B bei f = {welle_2d(weg_b[-1]):.2f}")
# Versatz und Ausrichtung der Beschriftung: Lauf B endet in der oberen
# rechten Ecke, dort muss der Text nach innen zeigen.
zeichne(ergebnisse,
[("Start A", weg_a, FARBEN["gut"], (16, 16), "left"),
("Start B", weg_b, FARBEN["fehler"], (-14, -30), "right")])

View file

@ -0,0 +1,189 @@
#!/usr/bin/env python3
# erzeuge_bnb_geometrie.py
"""
Erzeugt die geometrische Tafel zum Branch-and-Bound im MILP-Kapitel:
bilder_04/kap_milp_bnb_geometrie.svg
ERGAENZT den Suchbaum kap_milp_suchbaum.svg, ersetzt ihn nicht: Der Baum zeigt,
WELCHE Teilprobleme entstehen. Diese Tafel zeigt, WAS Verzweigen mit dem
Loesungsraum macht - und warum es erlaubt ist.
Links das Polyeder der LP-Relaxation mit seinem Optimum (3; 1,5). Rechts
dasselbe Polyeder, aus dem der Streifen 1 < x2 < 2 herausgeschnitten ist; uebrig
bleiben die Aeste A (x2 <= 1) und B (x2 >= 2) mit ihren eigenen Optima.
Der Punkt, auf den es ankommt: In dem herausgeschnittenen Streifen liegt KEIN
einziger ganzzahliger Punkt - es geht also nichts verloren. Der Streifen
enthaelt aber das Optimum der Relaxation, und deshalb sinkt die Schranke von 21
auf 20,67 bzw. 18. Verzweigen heisst, wertlosen Raum wegzuschneiden, bis der
beste verbleibende Punkt ganzzahlig ist.
Modell und LP-Loeser stammen aus erzeuge_branch_and_bound.py, damit Baum und
Geometrie nicht auseinanderlaufen koennen. pruefe_befund() rechnet nach, dass im
Streifen wirklich kein ganzzahliger Punkt liegt und dass die drei Zielwerte die
des Baums sind.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_bnb_geometrie.py
Benoetigt: numpy, scipy, matplotlib
"""
from __future__ import annotations
import itertools
import os
import sys
import numpy as np
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
from erzeuge_branch_and_bound import A, B, C, loese # noqa: E402
# Die Verzweigung, die der Baum als Erstes vornimmt: x2 <= 1 gegen x2 >= 2.
GRENZE = 1.0
# Was der Suchbaum ausweist; nur zum Gegenpruefen.
SCHRANKEN_IM_BAUM = {"P0": 21.0, "A": 62.0 / 3.0, "B": 18.0}
def gitter(masche: int = 420):
x1 = np.linspace(0, 4.6, masche)
x2 = np.linspace(0, 3.4, masche)
return np.meshgrid(x1, x2)
def zulaessig(xx1, xx2, unten=None, oben=None):
"""Maske des zulaessigen Bereichs, wahlweise mit zusaetzlichen Schranken."""
maske = np.ones_like(xx1, dtype=bool)
for zeile, grenze in zip(A, B):
maske &= (zeile[0] * xx1 + zeile[1] * xx2 <= grenze + 1e-9)
if unten is not None:
maske &= (xx2 >= unten - 1e-9)
if oben is not None:
maske &= (xx2 <= oben + 1e-9)
return maske
def ganzzahlige_punkte():
"""Alle ganzzahligen zulaessigen Punkte - fuer die Tafel und den Test."""
punkte = []
for p1, p2 in itertools.product(range(0, 6), range(0, 5)):
if np.all(A @ np.array([p1, p2], dtype=float) <= B + 1e-9):
punkte.append((p1, p2))
return punkte
def pruefe_befund(loesungen, punkte) -> None:
"""Die beiden Aussagen der Tafel muessen stimmen."""
im_streifen = [p for p in punkte if GRENZE < p[1] < GRENZE + 1]
if im_streifen:
raise SystemExit(
f"B&B-Geometrie: Im herausgeschnittenen Streifen liegen die "
f"ganzzahligen Punkte {im_streifen}. Dann waere das Verzweigen "
f"nicht verlustfrei, und die Tafel behauptete etwas Falsches.")
for name, soll in SCHRANKEN_IM_BAUM.items():
ist = loesungen[name][1]
if abs(ist - soll) > 1e-6:
raise SystemExit(
f"B&B-Geometrie: {name} hat den Zielwert {ist:.4f}, der "
f"Suchbaum weist {soll:.4f} aus.")
def zahl(wert: float) -> str:
return f"{wert:.2f}".rstrip("0").rstrip(".").replace(".", ",")
def zeichne(loesungen, punkte) -> None:
figur, achsen = plt.subplots(1, 2, figsize=(10.0, 4.5))
xx1, xx2 = gitter()
for achse in achsen:
for p1, p2 in punkte:
achse.plot(p1, p2, "o", color=FARBEN["gedaempft"], markersize=4.5,
zorder=5)
for zeile, grenze in zip(A, B):
x = np.linspace(0, 4.6, 100)
achse.plot(x, (grenze - zeile[0] * x) / zeile[1], "--",
color=FARBEN["linie"], linewidth=1.1, zorder=3)
achse.set_xlim(0, 4.6)
achse.set_ylim(0, 3.4)
achse.set_xlabel("$x_1$")
achse.grid(linestyle=":", alpha=0.4)
achsen[0].set_ylabel("$x_2$")
# --- links: die Relaxation, ungeschnitten -----------------------------
achsen[0].contourf(xx1, xx2, zulaessig(xx1, xx2).astype(float),
levels=[0.5, 1.5], colors=[FARBEN["haupt"]],
alpha=0.20, zorder=1)
x_p0, z_p0 = loesungen["P0"]
achsen[0].plot(*x_p0, "*", color=FARBEN["fehler"], markersize=17, zorder=6)
achsen[0].annotate(f"LP-Optimum ({zahl(x_p0[0])}; {zahl(x_p0[1])})\n"
f"Z = {zahl(z_p0)} — nicht ganzzahlig",
xy=x_p0, xytext=(12, 12), textcoords="offset points",
fontsize=8.5, color=FARBEN["fehler"],
fontweight="bold", zorder=7)
achsen[0].set_title("$P_0$ — die Relaxation", fontsize=10.5,
color=FARBEN["text"])
# --- rechts: derselbe Bereich, der Streifen heraus ---------------------
for name, unten, oben, farbe, beschriftung in (
("A", None, GRENZE, FARBEN["zweit"], f"Ast A: $x_2 \\leq 1$"),
("B", GRENZE + 1, None, FARBEN["warnung"],
f"Ast B: $x_2 \\geq 2$")):
achsen[1].contourf(xx1, xx2,
zulaessig(xx1, xx2, unten, oben).astype(float),
levels=[0.5, 1.5], colors=[farbe], alpha=0.22,
zorder=1)
x_ast, z_ast = loesungen[name]
achsen[1].plot(*x_ast, "*", color=farbe, markersize=15, zorder=6)
# Ast A liegt an der Schnittkante; sein Text gehoert nach unten in die
# eigene Flaeche, sonst laeuft er in den Streifen hinein.
achsen[1].annotate(f"{beschriftung}\nZ = {zahl(z_ast)}",
xy=x_ast,
xytext=(-12, -30) if name == "A" else (12, 14),
textcoords="offset points", fontsize=8.5,
ha="right" if name == "A" else "left",
color=farbe, fontweight="bold", zorder=7)
achsen[1].axhspan(GRENZE, GRENZE + 1, color=FARBEN["fehler"], alpha=0.11,
zorder=2)
achsen[1].axhline(GRENZE, color=FARBEN["fehler"], linewidth=1.3, zorder=4)
achsen[1].axhline(GRENZE + 1, color=FARBEN["fehler"], linewidth=1.3,
zorder=4)
achsen[1].text(4.5, GRENZE + 0.5,
"herausgeschnitten:\n$1 < x_2 < 2$\nkein ganzzahliger "
"Punkt darin", fontsize=8, color=FARBEN["fehler"],
ha="right", va="center", zorder=7)
achsen[1].set_title("nach dem Verzweigen über $x_2$", fontsize=10.5,
color=FARBEN["text"])
figur.suptitle(f"Verzweigen schneidet weg, wo kein ganzzahliger Punkt "
f"liegt — die Schranke sinkt von {zahl(z_p0)} auf "
f"{zahl(max(loesungen['A'][1], loesungen['B'][1]))}",
fontsize=10.5, color=FARBEN["text"], y=1.02)
speichere(figur, "kap_milp_bnb_geometrie")
if __name__ == "__main__":
unendlich = float("inf")
loesungen = {
"P0": loese([0.0, 0.0], [unendlich, unendlich]),
"A": loese([0.0, 0.0], [unendlich, GRENZE]),
"B": loese([0.0, GRENZE + 1], [unendlich, unendlich]),
}
punkte = ganzzahlige_punkte()
for name, (x, z) in loesungen.items():
print(f" {name:<3} x = ({x[0]:.4f}; {x[1]:.4f}) Z = {z:.4f}")
print(f" {len(punkte)} ganzzahlige zulässige Punkte")
pruefe_befund(loesungen, punkte)
print(" Streifen 1 < x₂ < 2 enthält keinen ganzzahligen Punkt")
print(" Zielwerte stimmen mit dem Suchbaum überein")
zeichne(loesungen, punkte)

View file

@ -0,0 +1,190 @@
#!/usr/bin/env python3
# erzeuge_dualitaets_bruecke.py
"""
Erzeugt die Doppeltafel zum starken Dualitaetssatz:
bilder_04/kap_lp_dualitaets_bruecke.svg
ERGAENZT das vorhandene kap04_primal_dual.svg, ersetzt es nicht: Jenes stellt
die beiden Probleme schematisch gegenueber - wer fragt was. Dieses zeigt ihre
Geometrie und damit den Satz selbst.
Links der primale Mengenraum (x1, x2): ein beschraenktes Polyeder, das Maximum
in einer Ecke. Rechts der duale Preisraum (y1, y2): ein nach oben offener
Bereich, das Minimum ebenfalls in einer Ecke. Die beiden Bereiche haben nichts
miteinander gemein - andere Achsen, andere Einheiten, der eine beschraenkt, der
andere nicht. Trotzdem steht an beiden Optima dieselbe Zahl. Das ist der Satz,
und das ist der einzige Grund fuer die Doppeltafel.
Der duale Raum hat drei Variablen; gezeichnet wird die Ebene y3 = 0. Das ist
keine Vereinfachung, sondern folgt aus dem komplementaeren Schlupf: Das
Marktlimit x1 <= 8 ist bei x1* = 7,5 nicht bindend, also ist y3* = 0. Der
Selbsttest rechnet nach, dass das Minimum in dieser Ebene wirklich das Minimum
des vollen Dualproblems ist - sonst zeigte die rechte Tafel einen zu grossen
Wert und der optische Beweis waere keiner.
Modell und Loesung stammen aus dem Kapitelprogramm Dualitaet_Nachweis.py; hier
wird nichts nachgebaut und nichts eingetragen.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_dualitaets_bruecke.py
Benoetigt: numpy, scipy, matplotlib
"""
from __future__ import annotations
import itertools
import os
import sys
import numpy as np
HIER = os.path.dirname(os.path.abspath(__file__))
BASIS = os.path.dirname(HIER)
sys.path.insert(0, HIER)
sys.path.insert(0, os.path.join(
BASIS, "Operations_Research_mit_Python_Version_04_Programme"))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
from Dualitaet_Nachweis import (A_PRIMAL, B_PRIMAL, # noqa: E402
C_PRIMAL, loese)
def ecken(A: np.ndarray, b: np.ndarray) -> np.ndarray:
"""Ecken des Bereichs {x >= 0 : A x <= b}, gegen den Uhrzeigersinn.
Alle Paare von Randgeraden schneiden (die Achsen zaehlen mit), die
unzulaessigen Schnittpunkte verwerfen, den Rest um den Schwerpunkt
sortieren. Fuer zwei Dimensionen ist das vollstaendig."""
zeilen = np.vstack([A, [[-1.0, 0.0], [0.0, -1.0]]])
rechte = np.concatenate([b, [0.0, 0.0]])
punkte = []
for i, j in itertools.combinations(range(len(zeilen)), 2):
M = zeilen[[i, j]]
if abs(np.linalg.det(M)) < 1e-9:
continue
p = np.linalg.solve(M, rechte[[i, j]])
if np.all(zeilen @ p <= rechte + 1e-9):
punkte.append(p)
punkte = np.unique(np.round(punkte, 9), axis=0)
mitte = punkte.mean(axis=0)
winkel = np.arctan2(punkte[:, 1] - mitte[1], punkte[:, 0] - mitte[0])
return punkte[np.argsort(winkel)]
def zeichne(x_stern, z_stern, y_stern, w_stern) -> None:
figur, achsen = plt.subplots(1, 2, figsize=(10.2, 4.6))
beschriftung = f"{z_stern:,.0f}".replace(",", ".")
# --- links: der primale Mengenraum ------------------------------------
links = achsen[0]
polygon = ecken(A_PRIMAL, B_PRIMAL)
links.fill(polygon[:, 0], polygon[:, 1], color=FARBEN["haupt"],
alpha=0.18, zorder=2)
links.plot(np.append(polygon[:, 0], polygon[0, 0]),
np.append(polygon[:, 1], polygon[0, 1]), "-",
color=FARBEN["haupt"], linewidth=1.8, zorder=3)
x1 = np.linspace(0, 11, 200)
# Ein Niveau unter dem Optimum, das Optimum selbst, eines darueber - das
# obere liegt sichtbar ausserhalb des Polyeders, und genau das ist die
# Aussage: weiter geht es nicht.
for niveau, stil in ((1500.0, ":"), (z_stern, "--"), (3000.0, ":")):
# 150 x1 + 250 x2 = niveau
links.plot(x1, (niveau - C_PRIMAL[0] * x1) / C_PRIMAL[1], stil,
color=FARBEN["warnung"], linewidth=1.4, zorder=4)
links.annotate("", xy=(4.2, 4.2), xytext=(2.4, 3.1),
arrowprops=dict(arrowstyle="-|>", color=FARBEN["warnung"],
linewidth=2.0), zorder=5)
links.text(4.35, 4.25, r"$\mathbf{c} = (150, 250)^\top$", fontsize=8.5,
color=FARBEN["warnung"])
links.plot(*x_stern, "*", color=FARBEN["gut"], markersize=17, zorder=6)
links.annotate(f"$\\mathbf{{x}}^* = ({x_stern[0]:.1f}\\,;\\,"
f"{x_stern[1]:.0f})$\n$Z^* = {beschriftung}$",
xy=x_stern, xytext=(4.55, 1.05), textcoords="data",
fontsize=9, color=FARBEN["gut"], fontweight="bold",
ha="left", zorder=7,
arrowprops=dict(arrowstyle="-", color=FARBEN["gut"],
linewidth=1.0, alpha=0.7))
links.set_xlim(0, 11)
links.set_ylim(0, 9)
links.set_xlabel("$x_1$ — Bots Typ A")
links.set_ylabel("$x_2$ — Bots Typ B")
links.set_title("primal: Mengen — maximieren, beschränkt",
fontsize=10.5, color=FARBEN["text"])
# --- rechts: der duale Preisraum, Ebene y3 = 0 ------------------------
rechts = achsen[1]
g1 = np.linspace(0, 55, 320)
g2 = np.linspace(0, 55, 320)
yy1, yy2 = np.meshgrid(g1, g2)
# A^T y >= c, hier ohne y3: 2 y1 + 4 y2 >= 150 und 5 y1 + 6 y2 >= 250
zulaessig = ((A_PRIMAL[0, 0] * yy1 + A_PRIMAL[1, 0] * yy2
>= C_PRIMAL[0] - 1e-9)
& (A_PRIMAL[0, 1] * yy1 + A_PRIMAL[1, 1] * yy2
>= C_PRIMAL[1] - 1e-9))
rechts.contourf(yy1, yy2, zulaessig.astype(float), levels=[0.5, 1.5],
colors=[FARBEN["zweit"]], alpha=0.18, zorder=2)
for spalte, farbe in ((0, FARBEN["zweit"]), (1, FARBEN["zweit"])):
# A[0, spalte] * y1 + A[1, spalte] * y2 = c[spalte]
rechts.plot(g1, (C_PRIMAL[spalte] - A_PRIMAL[0, spalte] * g1)
/ A_PRIMAL[1, spalte], "-", color=farbe, linewidth=1.8,
zorder=3)
for niveau, stil in ((1200.0, ":"), (w_stern, "--"), (3600.0, ":")):
# 40 y1 + 60 y2 = niveau
rechts.plot(g1, (niveau - B_PRIMAL[0] * g1) / B_PRIMAL[1], stil,
color=FARBEN["warnung"], linewidth=1.4, zorder=4)
rechts.plot(y_stern[0], y_stern[1], "*", color=FARBEN["gut"],
markersize=17, zorder=6)
rechts.annotate(f"$\\mathbf{{y}}^* = ({y_stern[0]:.2f}\\,;\\,"
f"{y_stern[1]:.2f})$\n$W^* = "
f"{f'{w_stern:,.0f}'.replace(',', '.')}$ €",
xy=(y_stern[0], y_stern[1]), xytext=(14, 16),
textcoords="offset points", fontsize=9,
color=FARBEN["gut"], fontweight="bold", zorder=7)
rechts.annotate("zulässig: nach oben offen —\nteurer geht immer",
xy=(0.04, 0.97), xycoords="axes fraction", fontsize=8.5,
color=FARBEN["text"], va="top")
rechts.set_xlim(0, 55)
rechts.set_ylim(0, 55)
rechts.set_xlabel("$y_1$ — Preis je vCPU-Stunde")
rechts.set_ylabel("$y_2$ — Preis je GB RAM")
rechts.set_title("dual: Preise — minimieren, unbeschränkt",
fontsize=10.5, color=FARBEN["text"])
figur.suptitle(f"Zwei verschiedene Räume, ein gemeinsamer Wert: "
f"{beschriftung}",
fontsize=11.5, color=FARBEN["text"], y=1.01)
speichere(figur, "kap_lp_dualitaets_bruecke")
if __name__ == "__main__":
primal, dual = loese()
x_stern, z_stern = primal.x, -primal.fun
y_stern, w_stern = dual.x, dual.fun
print(f" primal: x* = ({x_stern[0]:.4f}, {x_stern[1]:.4f}) "
f"Z* = {z_stern:.4f}")
print(f" dual: y* = ({y_stern[0]:.4f}, {y_stern[1]:.4f}, "
f"{y_stern[2]:.4f}) W* = {w_stern:.4f}")
if abs(z_stern - w_stern) > 1e-6:
raise SystemExit(
f"Dualitaets-Bruecke: Z* = {z_stern:.6f}, W* = {w_stern:.6f}. "
f"Die Tafel behauptet einen gemeinsamen Wert - er ist keiner.")
# Die rechte Tafel zeigt die Ebene y3 = 0. Das ist nur zulaessig, wenn
# das Optimum dort liegt.
if abs(y_stern[2]) > 1e-9:
raise SystemExit(
f"Dualitaets-Bruecke: y3* = {y_stern[2]:.6f} ist nicht null. Die "
f"gezeichnete Ebene y3 = 0 enthaelt das Optimum dann nicht.")
print(f" starker Dualitätssatz bestätigt: "
f"{z_stern:,.0f}".replace(",", "."))
print(f" y3* = 0 — die gezeichnete Ebene enthält das Optimum")
zeichne(x_stern, z_stern, y_stern, w_stern)

View file

@ -91,9 +91,43 @@ def loese() -> tuple[list[tuple[int, int, int, int]], int]:
return plan, loeser.Value(makespan)
def untere_schranke() -> int:
"""Wie in JobShop_Intervalle.py: laengster Auftrag bzw. hoechste Maschinenlast.
Kein Plan kann kuerzer sein als der laengste Auftrag (seine Schritte laufen
nacheinander) und auch nicht kuerzer als die am staerksten belegte Maschine
(ihre Schritte koennen sich nicht ueberlappen)."""
je_auftrag = max(sum(dauer for _, dauer in auftrag)
for auftrag in AUFTRAEGE)
last = [0] * len(MASCHINENNAMEN)
for auftrag in AUFTRAEGE:
for maschine, dauer in auftrag:
last[maschine] += dauer
return max(je_auftrag, max(last))
def stillstaende(plan):
"""Luecken ZWISCHEN zwei Arbeitsgaengen derselben Maschine.
Nur die inneren: Was eine Maschine vor ihrem ersten oder nach ihrem letzten
Arbeitsgang tut, ist keine Luecke im Plan, sondern sein Rand."""
luecken = []
for maschine in range(len(MASCHINENNAMEN)):
zeiten = sorted((beginn, beginn + dauer)
for _, m, beginn, dauer in plan if m == maschine)
for (_, ende), (naechster_beginn, _) in zip(zeiten, zeiten[1:]):
if naechster_beginn > ende:
luecken.append((maschine, ende, naechster_beginn))
return luecken
def zeichne_statisch(plan, makespan: int) -> None:
"""Matplotlib-Fassung fuer das PDF."""
figur, achse = plt.subplots(figsize=(9.0, 3.2))
figur, achse = plt.subplots(figsize=(9.0, 3.4))
for maschine, von, bis in stillstaende(plan):
achse.barh(y=maschine, width=bis - von, left=von, height=0.55,
facecolor="none", edgecolor="#be123c", linewidth=1.2,
hatch="///", zorder=2)
for auftrag, maschine, beginn, dauer in plan:
achse.barh(y=maschine, width=dauer, left=beginn, height=0.55,
color=FARBEN[auftrag % len(FARBEN)],
@ -108,7 +142,21 @@ def zeichne_statisch(plan, makespan: int) -> None:
achse.set_xlabel("Zeiteinheiten")
achse.set_xlim(0, makespan)
achse.set_xticks(range(makespan + 1))
achse.set_title(f"Job-Shop-Plan, Makespan = {makespan}", fontsize=11)
schranke = untere_schranke()
achse.axvline(schranke, color="#059669", linestyle="--", linewidth=1.5,
zorder=5)
# x in Datenkoordinaten, y als Anteil der Achsenhoehe: Die y-Achse ist
# umgekehrt, ein fester y-Wert laege sonst ausserhalb des Bildes.
unten = achse.get_xaxis_transform()
# Der Makespan steht schon im Titel - hier nur die Schranke, sonst
# stossen beide Beschriftungen aneinander.
achse.text(schranke - 0.12, 0.03, f"untere Schranke {schranke}",
transform=unten, fontsize=8.5, color="#059669", ha="right",
va="bottom", zorder=6)
achse.set_title(f"Job-Shop-Plan: Makespan {makespan}, untere Schranke "
f"{schranke} — schraffiert steht die Maschine still",
fontsize=11)
achse.grid(axis="x", linestyle=":", alpha=0.5)
for rand in ("top", "right", "left"):
achse.spines[rand].set_visible(False)
@ -170,6 +218,40 @@ def zeichne_interaktiv(plan, makespan: int) -> None:
if __name__ == "__main__":
plan, makespan = loese()
print(f"Makespan: {makespan}")
schranke = untere_schranke()
print(f"Makespan: {makespan}, untere Schranke: {schranke}")
for maschine, von, bis in stillstaende(plan):
print(f" Stillstand: {MASCHINENNAMEN[maschine]} von {von} bis {bis}")
# Gegenprobe zu den Zahlen, die das Kapitel abdruckt.
if schranke > makespan:
raise SystemExit(f"Gantt: Die untere Schranke {schranke} liegt ueber "
f"dem Makespan {makespan} - eine der beiden Zahlen "
f"ist falsch.")
if (schranke, makespan) != (10, 11):
raise SystemExit(f"Gantt: Schranke {schranke} / Makespan {makespan}; "
f"das Kapitel druckt 10 / 11 ab.")
if not any(m == 1 and von == 4 for m, von, _ in stillstaende(plan)):
raise SystemExit("Gantt: Die Luecke in der Dreherei bei Zeitpunkt 4 "
"fehlt - der Text daneben bespricht genau sie.")
# Der Text daneben rechnet vor: Die Engpassmaschine ist von 0 bis zum
# Makespan belegt, ihre Arbeit ist die Schranke, der Rest Stillstand.
last = [0] * len(MASCHINENNAMEN)
for auftrag in AUFTRAEGE:
for maschine, dauer in auftrag:
last[maschine] += dauer
engpass = last.index(max(last))
leerlauf = sum(bis - von for m, von, bis in stillstaende(plan)
if m == engpass)
if last[engpass] + leerlauf != makespan:
raise SystemExit(
f"Gantt: {MASCHINENNAMEN[engpass]} arbeitet {last[engpass]} und "
f"steht {leerlauf} still, zusammen "
f"{last[engpass] + leerlauf} statt {makespan}. Der Text daneben "
f"rechnet genau diese Summe vor.")
print(f" Engpass {MASCHINENNAMEN[engpass]}: {last[engpass]} Arbeit + "
f"{leerlauf} Stillstand = {makespan}")
zeichne_statisch(plan, makespan)
zeichne_interaktiv(plan, makespan)

View file

@ -0,0 +1,214 @@
#!/usr/bin/env python3
# erzeuge_or_architektur.py
"""
Erzeugt das Schichtenbild der OR-Plattform im Kapitel Praxisfallen:
bilder_04/kap15_or_plattform.svg
ERSETZT die bisherige, von Hand gezeichnete Fassung. Der Anlass war ein
Widerspruch: Die Tabelle im Abschnitt nennt SECHS Schichten, das gezeichnete
Bild daneben zeigte nur FUENF. Die sechste - "Betrieb" mit Monitoring,
Audit-Trail und Alarmen - fehlte; "Audit-Trail" kam nur noch als Beschriftung
eines Pfeils vor. Genau so entstehen Bilder, die dem Text daneben
widersprechen, und genau deshalb bekommt dieses hier einen Generator.
Die Schichten werden NICHT in diesem Skript aufgezaehlt, sondern aus der
Tabelle des Kapitels gelesen. Damit gibt es nur eine Quelle: Wer eine Zeile in
50_Praxis.md ergaenzt, ergaenzt das Bild mit. Fehlt die Tabelle oder aendert
sich ihr Aufbau, bricht der Generator ab, statt ein halbes Bild zu schreiben.
Dargestellt wird die Kette als das, was sie ist: Die ersten fuenf Schichten
sind ein Datenweg von den Quellsystemen bis zur Ausfuehrung. "Betrieb" ist
keine Station auf diesem Weg, sondern liegt neben allen - Monitoring und
Audit-Trail betreffen jede Schicht. Als sechster Kasten in der Reihe waere sie
falsch dargestellt; deshalb steht sie als Spange daneben. Dass sie beim
Handzeichnen ganz verschwand, hat vermutlich denselben Grund.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_or_architektur.py
Benoetigt: matplotlib
"""
from __future__ import annotations
import os
import re
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch # noqa: E402
BASIS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
KAPITEL = os.path.join(BASIS, "Operations_Research_mit_Python_Version_04",
"50_Praxis.md")
UEBERSCHRIFT = "## Architektur einer produktionsreifen OR-Plattform"
# Was zwischen den Schichten fliesst. Steht nicht in der Tabelle - die
# beschreibt die Schichten, nicht die Uebergaenge. Fuenf Stationen in der
# Kette ergeben VIER Pfeile; lies_schichten() rechnet das nach.
UEBERGAENGE = [
"Events · REST · Polling",
"geprüfter Snapshot (JSON)",
"Ergebnis + Kostenzerlegung",
"Freigabe",
]
# Die Schicht, die neben der Kette steht statt in ihr.
QUERSCHNITT = "Betrieb"
def saeubere(zelle: str) -> str:
"""Markdown-Auszeichnung und Indexmarken aus einer Tabellenzelle nehmen."""
zelle = re.sub(r"\{idx:[^}]*\}", "", zelle)
zelle = zelle.replace("**", "").replace("`", "")
return zelle.strip()
def lies_schichten() -> list[tuple[str, str, str]]:
"""Die Schichtentabelle des Kapitels als Liste (Schicht, Aufgabe, Werkzeuge).
Einzige Quelle fuer den Bildinhalt - siehe Modulkommentar."""
with open(KAPITEL, encoding="utf-8") as datei:
text = datei.read()
if UEBERSCHRIFT not in text:
raise SystemExit(f"OR-Architektur: Überschrift '{UEBERSCHRIFT}' nicht "
f"in {os.path.basename(KAPITEL)} gefunden.")
rest = text.split(UEBERSCHRIFT, 1)[1]
zeilen = []
gefunden = False
for zeile in rest.splitlines():
if zeile.startswith("| Schicht |"):
gefunden = True
continue
if not gefunden:
continue
if zeile.startswith("| ---"):
continue
if not zeile.startswith("|"):
break
felder = [saeubere(f) for f in zeile.strip().strip("|").split("|")]
if len(felder) != 3:
raise SystemExit(f"OR-Architektur: Tabellenzeile mit "
f"{len(felder)} statt 3 Spalten: {zeile!r}")
zeilen.append(tuple(felder))
if not zeilen:
raise SystemExit("OR-Architektur: Die Schichtentabelle wurde nicht "
"gefunden. Hat sich der Abschnitt geändert?")
if zeilen[-1][0] != QUERSCHNITT:
raise SystemExit(
f"OR-Architektur: Die letzte Tabellenzeile heißt "
f"'{zeilen[-1][0]}', erwartet wurde '{QUERSCHNITT}'. Diese "
f"Schicht wird als Spange neben der Kette gezeichnet, nicht in "
f"ihr - die Zuordnung muss stimmen.")
# Die Kette sind alle Zeilen ausser der Querschnittsschicht; zwischen n
# Stationen liegen n-1 Pfeile.
if len(zeilen) - 2 != len(UEBERGAENGE):
raise SystemExit(
f"OR-Architektur: {len(zeilen) - 1} Stationen in der Kette "
f"brauchen {len(zeilen) - 2} beschriftete Übergänge, es sind "
f"aber {len(UEBERGAENGE)}. Beim Ergänzen einer Schicht gehört "
f"ein Übergang dazu.")
return zeilen
def umbreche(text: str, breite: int) -> str:
"""Einfacher Wortumbruch - textwrap.fill ohne den Import."""
zeilen, aktuell = [], ""
for wort in text.split():
if aktuell and len(aktuell) + 1 + len(wort) > breite:
zeilen.append(aktuell)
aktuell = wort
else:
aktuell = f"{aktuell} {wort}".strip()
if aktuell:
zeilen.append(aktuell)
return "\n".join(zeilen)
def zeichne(schichten) -> None:
kette = schichten[:-1]
quer = schichten[-1]
figur, achse = plt.subplots(figsize=(8.8, 6.6))
achse.set_axis_off()
kastenbreite, kastenhoehe = 6.4, 1.24
abstand = 1.88
links = 0.0
for stufe, (name, aufgabe, werkzeuge) in enumerate(kette):
unten = -stufe * abstand
achse.add_patch(FancyBboxPatch(
(links, unten), kastenbreite, kastenhoehe,
boxstyle="round,pad=0.02,rounding_size=0.10",
facecolor=FARBEN["flaeche"], edgecolor=FARBEN["haupt"],
linewidth=1.6, zorder=3))
achse.text(links + 0.18, unten + kastenhoehe - 0.26, name,
fontsize=10.5, color=FARBEN["haupt"], fontweight="bold",
va="center", zorder=4)
achse.text(links + 0.18, unten + kastenhoehe - 0.52,
umbreche(aufgabe, 66), fontsize=8.5,
color=FARBEN["text"], va="top", zorder=4)
achse.text(links + 0.18, unten + 0.17,
umbreche(werkzeuge, 70), fontsize=8,
color=FARBEN["gedaempft"], va="bottom", style="italic",
zorder=4)
if stufe < len(kette) - 1:
pfeilspitze = unten - abstand + kastenhoehe
achse.add_patch(FancyArrowPatch(
(links + kastenbreite / 2, unten),
(links + kastenbreite / 2, pfeilspitze),
arrowstyle="-|>", mutation_scale=16,
color=FARBEN["zweit"], linewidth=1.6, zorder=2))
achse.text(links + kastenbreite / 2 + 0.16,
(unten + pfeilspitze) / 2, UEBERGAENGE[stufe],
fontsize=8, color=FARBEN["zweit"], va="center",
zorder=4)
# Die Querschnittsschicht als Spange neben der ganzen Kette.
unten_ganz = -(len(kette) - 1) * abstand
spange_links = links + kastenbreite + 0.45
achse.add_patch(FancyBboxPatch(
(spange_links, unten_ganz), 0.92,
(len(kette) - 1) * abstand + kastenhoehe,
boxstyle="round,pad=0.02,rounding_size=0.10",
facecolor=FARBEN["warnung"], alpha=0.13,
edgecolor=FARBEN["warnung"], linewidth=1.6, zorder=3))
achse.text(spange_links + 0.46,
unten_ganz + ((len(kette) - 1) * abstand + kastenhoehe) / 2,
f"{quer[0]}{quer[1]}\n{quer[2]}",
fontsize=9, color=FARBEN["warnung"], fontweight="bold",
ha="center", va="center", rotation=90, zorder=4)
achse.text(links, kastenhoehe + 0.42,
"Der Optimierer ist kein Skript, sondern ein Dienst",
fontsize=11.5, color=FARBEN["text"], fontweight="bold")
achse.text(links, -(len(kette) - 1) * abstand - 0.42,
f"{quer[0]}“ ist keine Station auf dem Weg — Monitoring und "
f"Audit-Trail betreffen jede Schicht.\nDeshalb steht sie "
f"neben der Kette, nicht in ihr.",
fontsize=8.5, color=FARBEN["gedaempft"], va="top")
achse.set_xlim(-0.3, spange_links + 1.5)
achse.set_ylim(-(len(kette) - 1) * abstand - 1.3, kastenhoehe + 0.85)
achse.set_aspect("equal")
speichere(figur, "kap15_or_plattform")
if __name__ == "__main__":
schichten = lies_schichten()
for name, aufgabe, werkzeuge in schichten:
markierung = " (Spange)" if name == QUERSCHNITT else ""
print(f" {name:<22}{markierung}")
print(f" {len(schichten)} Schichten aus der Tabelle in "
f"{os.path.basename(KAPITEL)} gelesen — "
f"{len(schichten) - 1} in der Kette, 1 quer.")
zeichne(schichten)

View file

@ -107,6 +107,72 @@ def untere_huelle(punkte):
return huelle
def tiefste_delle(front, huelle):
"""Der unerreichbare Punkt mit dem groessten Abstand zur Huelle.
Liefert (Punkt, linke Huellenecke, rechte Huellenecke, Abstand). Genau an
diesem Punkt ist die Luecke am besten zu sehen, und genau dort wird sie
beschriftet - gesucht wird sie, nicht ausgewaehlt.
Gemessen wird SENKRECHT zur Huellenkante, nicht senkrecht nach oben. Der
senkrechte Abstand ist der Abstand eines Punktes zu einer Geraden; der
Abstand in y-Richtung haengt dagegen davon ab, welche Zielgroesse man auf
die Hochachse legt - bei vertauschten Achsen faende er eine andere Delle."""
unerreichbar = [p for p in front if p not in huelle]
if not unerreichbar:
return None
bestes = None
for punkt in unerreichbar:
for links, rechts in zip(huelle, huelle[1:]):
if not links[0] <= punkt[0] <= rechts[0]:
continue
if rechts[0] == links[0]:
continue
steigung = (rechts[1] - links[1]) / (rechts[0] - links[0])
auf_huelle = links[1] + steigung * (punkt[0] - links[0])
abstand = (punkt[1] - auf_huelle) / np.hypot(1.0, steigung)
if bestes is None or abstand > bestes[3]:
bestes = (punkt, links, rechts, abstand)
return bestes
def zeichne_luecke(achse, front, huelle) -> None:
"""Warum die gewichtete Summe die Delle nicht erreicht - und was hilft.
Gezeichnet werden zwei Geraden derselben Steigung: die Tangente an die
Huelle und die parallele Gerade durch den unerreichbaren Punkt. Eine
gewichtete Summe minimiert genau diese Steigung; sie waehlt also immer die
untere der beiden Geraden und damit nie den Punkt darueber. Der
waagerechte Strich zeigt die Alternative: eine CO2-Schranke, unter der
dieser Punkt der billigste ist - so ist die Front oben ueberhaupt
entstanden."""
delle = tiefste_delle(front, huelle)
if delle is None:
return
punkt, links, rechts, _abstand = delle
steigung = ((rechts[1] - links[1]) / (rechts[0] - links[0])
if rechts[0] != links[0] else 0.0)
x = np.array(achse.get_xlim())
achse.plot(x, links[1] + steigung * (x - links[0]), "-", color=INDIGO,
linewidth=1.1, alpha=0.75, zorder=1)
achse.plot(x, punkt[1] + steigung * (x - punkt[0]), ":", color=AMBER,
linewidth=1.6, zorder=2)
achse.annotate("dieselbe Steigung, eine Stufe höher —\n"
"jede gewichtete Summe nimmt die untere",
xy=(punkt[0], punkt[1]), xytext=(12, 26),
textcoords="offset points", fontsize=7.5, color=AMBER,
zorder=6)
achse.axhline(punkt[1], color="#059669", linestyle="--", linewidth=1.2,
alpha=0.9, zorder=1)
achse.annotate(f"ε-Constraint: CO₂ ≤ {punkt[1]:,} kg".replace(",", ".")
+ "\nfindet genau diesen Punkt",
xy=(x[0], punkt[1]), xytext=(6, -22),
textcoords="offset points", fontsize=7.5, color="#059669",
zorder=6)
# --- Statische Fassung ------------------------------------------------------
def zeichne_statisch(front, huelle) -> None:
@ -132,6 +198,9 @@ def zeichne_statisch(front, huelle) -> None:
achse.set_xlabel("Transportkosten (EUR)")
achse.set_ylabel("CO₂-Ausstoß (kg)")
achse.set_title("Pareto-Front: Kosten gegen CO₂", fontsize=11)
# Erst jetzt, damit die Geraden die endgueltigen Achsengrenzen benutzen.
achse.autoscale(enable=False)
zeichne_luecke(achse, front, huelle)
achse.grid(linestyle=":", alpha=0.5)
for rand in ("top", "right"):
achse.spines[rand].set_visible(False)
@ -203,5 +272,49 @@ if __name__ == "__main__":
huelle = untere_huelle(front)
print(f"Pareto-Punkte: {len(front)}, davon gestuetzt: {len(huelle)}, "
f"unerreichbar: {len(front) - len(huelle)}")
# Die beiden Behauptungen des Luecken-Bildes nachrechnen, statt sie
# hinzuschreiben.
delle = tiefste_delle(front, huelle)
if delle is not None:
punkt, links, rechts, abstand = delle
steigung = (rechts[1] - links[1]) / (rechts[0] - links[0])
def auf_tangente(x):
return links[1] + steigung * (x - links[0])
# Eine Stuetzgerade der UNTEREN Huelle liegt unter allen Punkten und
# beruehrt die beiden Ecken der Kante. Beides zusammen macht sie zur
# Tangente; nur eines davon reicht nicht.
for ecke in huelle:
if ecke[1] < auf_tangente(ecke[0]) - 1e-6:
raise SystemExit(
f"Pareto-Luecke: Die Huellenecke {ecke} liegt UNTER der "
f"gezeichneten Geraden - dann stuetzt sie die Huelle "
f"nicht.")
for ecke in (links, rechts):
if abs(ecke[1] - auf_tangente(ecke[0])) > 1e-6:
raise SystemExit(
f"Pareto-Luecke: Die Gerade beruehrt die Huellenecke "
f"{ecke} nicht - dann ist sie keine Tangente an diese "
f"Kante.")
if punkt[1] <= auf_tangente(punkt[0]) + 1e-6:
raise SystemExit(
f"Pareto-Luecke: {punkt} liegt nicht ueber der Tangente - "
f"dann waere der Punkt von einer gewichteten Summe erreichbar.")
gefunden = plane(KOSTEN, co2_grenze=punkt[1])
if gefunden != punkt:
raise SystemExit(
f"Pareto-Luecke: Die Schranke CO2 <= {punkt[1]} liefert "
f"{gefunden}, nicht {punkt}. Die Bildunterschrift zum "
f"ε-Constraint waere falsch.")
# Die Zahl getrennt formatieren: Ein .replace(",", ".") auf den ganzen
# f-String erwischt auch das Komma im Tupel.
abstand_text = f"{abstand:.1f}".replace(".", ",")
print(f" tiefste Delle: {punkt}, Abstand zur Hülle "
f"{abstand_text} (senkrecht)")
print(f" Tangente stützt die Hülle, der Punkt liegt darüber")
print(f" ε-Constraint CO₂ ≤ {punkt[1]} liefert genau diesen Punkt")
zeichne_statisch(front, huelle)
zeichne_interaktiv(front, huelle)

View file

@ -50,6 +50,7 @@ BASIS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BILDER = os.path.join(BASIS, "bilder_04")
INDIGO, CYAN, AMBER, GRUEN = "#4338ca", "#0891b2", "#b45309", "#059669"
VIOLETT = "#7c3aed" # Niveaulinien und Zielfunktionsgradient
# Modell wortgleich zu Visualisierung_Loesungsraum.py
C = np.array([150.0, 250.0])
@ -200,12 +201,46 @@ def zeichne_polyeder(ecken: np.ndarray, bestes: np.ndarray) -> None:
schreibe(figur, "kap_fundament_polyeder")
def zeichne_niveaulinien(achse, besucht: list[np.ndarray]) -> None:
"""Eine Niveaulinie der Zielfunktion durch JEDE besuchte Ecke.
Die Niveaus werden aus dem Weg gerechnet, nicht gewaehlt: Jede Ecke des
Simplexwegs liegt damit auf genau einer Linie, und der Abstand der Linien
ist der Fortschritt eines Schritts. Damit zeigt das Bild nicht nur, WELCHEN
Weg der Simplex nimmt, sondern warum - er schiebt die Linie so weit nach
aussen, wie das Polyeder es zulaesst."""
x = np.linspace(-0.9, 11.4, 100)
for punkt in besucht:
niveau = float(C @ punkt)
achse.plot(x, (niveau - C[0] * x) / C[1], linestyle=":",
color=VIOLETT, linewidth=1.3, alpha=0.9, zorder=2)
# Beschriftung dort, wo die Linie im Bild noch Platz hat. Die
# Nulllinie laeuft nahe am unteren Rand - ihre Beschriftung gehoert
# darueber, sonst wird sie abgeschnitten.
unten = niveau < 500
x_text = 1.7 if unten else 10.1
y_linie = (niveau - C[0] * x_text) / C[1]
achse.text(x_text, y_linie + (0.16 if unten else -0.28),
f"Z = {euro(niveau)}", fontsize=7.5, color=VIOLETT,
ha="center", va="bottom" if unten else "top", zorder=6)
# Der Gradient: die Richtung, in die sich die Linien verschieben lassen.
start = np.array([2.6, 2.2])
ende = start + C / np.linalg.norm(C) * 2.6
achse.annotate("", xy=tuple(ende), xytext=tuple(start),
arrowprops=dict(arrowstyle="-|>", color=VIOLETT,
linewidth=2.2), zorder=6)
achse.text(ende[0] + 0.15, ende[1], "$\\mathbf{c} = (150; 250)$",
fontsize=8.5, color=VIOLETT, va="center", zorder=6)
def zeichne_simplexweg(ecken: np.ndarray, bestes: np.ndarray) -> None:
"""LP-Kapitel: derselbe Bereich, aber der Weg der Handrechnung."""
figur, achse = plt.subplots(figsize=(7.2, 5.2))
zeichne_rahmen(achse, ecken)
besucht = [np.array(p) for p in SIMPLEXWEG]
zeichne_niveaulinien(achse, besucht)
unbesucht = [e for e in ecken
if not any(np.allclose(e, p) for p in besucht)]
for ecke in unbesucht:

View file

@ -0,0 +1,168 @@
#!/usr/bin/env python3
# erzeuge_propagation_ablauf.py
"""
Erzeugt die Ablaufgrafik zur Propagation im Kapitel CP-SAT:
bilder_04/kap_cpsat_propagation_ablauf.svg
ERGAENZT das vorhandene kap06_cp_sat_propagation.svg, ersetzt es nicht: Jenes
zeigt schematisch das Zusammenspiel von Propagation und Konfliktlernen. Dieses
zeigt die Propagation selbst - Runde fuer Runde, an Zahlen.
Gezeigt wird GENAU die Handrechnung des Kapitels:
x1 in {1,2,3,4}, x2 in {2,3,4,5}, Bedingung x1 + x2 = 7
Ausgangslage 4 x 4 = 16 Kombinationen
nach Runde 1 x1 -> {2,3,4} 12 Kombinationen
nach Runde 2 x2 -> {3,4,5} 9 Kombinationen
Bewusst nicht das Beispiel aus der Auftragsbeschreibung ([0,10] mit x1+x2<=6):
Die Handrechnung steht unmittelbar neben dem Bild. Ein Bild mit anderen Zahlen
als der Text daneben zwingt den Leser, beide zu vergleichen, statt eines zu
verstehen.
Die Domains werden PROPAGIERT, nicht eingetragen: revidiere() streicht jeden
Wert, fuer den es im Partner keinen Partner gibt - das ist Kantenkonsistenz,
und zwar in derselben Reihenfolge wie in der Handrechnung. Der Selbsttest
vergleicht das Ergebnis mit den drei im Kapitel abgedruckten Zahlen.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_propagation_ablauf.py
Benoetigt: matplotlib
"""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
from matplotlib.patches import Rectangle # noqa: E402
# Die Aufgabe der Handrechnung.
START_X1 = {1, 2, 3, 4}
START_X2 = {2, 3, 4, 5}
SUMME = 7
ANZEIGE = range(1, 7) # 1 bis 6 - alle je vorkommenden Werte
# Was das Kapitel abdruckt; nur zum Gegenpruefen.
KOMBINATIONEN_IM_BUCH = [16, 12, 9]
ENDDOMAINS_IM_BUCH = ({2, 3, 4}, {3, 4, 5})
def revidiere(eigene: set[int], partner: set[int]) -> set[int]:
"""Streicht jeden Wert, zu dem es im Partner keinen Partner gibt."""
return {v for v in eigene if (SUMME - v) in partner}
def propagiere():
"""Die zwei Runden der Handrechnung, jede als eigener Zustand."""
d1, d2 = set(START_X1), set(START_X2)
verlauf = [("Ausgangslage", set(d1), set(d2), None)]
d1 = revidiere(d1, d2)
verlauf.append((f"Runde 1 — von $x_1$ aus", set(d1), set(d2), 0))
d2 = revidiere(d2, d1)
verlauf.append((f"Runde 2 — von $x_2$ aus", set(d1), set(d2), 1))
return verlauf
def zeichne(verlauf) -> None:
figur, achse = plt.subplots(figsize=(8.6, 4.6))
achse.set_axis_off()
zellbreite, zellhoehe = 1.0, 0.62
zeilenabstand = 1.05
blockabstand = 2.6
for stufe, (titel, d1, d2, geaendert) in enumerate(verlauf):
oben = -stufe * blockabstand
achse.text(-0.4, oben + 0.75, titel, fontsize=10,
color=FARBEN["text"], fontweight="bold", ha="left",
va="center")
for reihe, (name, domain, vorher) in enumerate((
("$x_1$", d1, verlauf[stufe - 1][1] if stufe else d1),
("$x_2$", d2, verlauf[stufe - 1][2] if stufe else d2))):
y = oben - reihe * zeilenabstand
achse.text(-0.4, y + zellhoehe / 2, name, fontsize=11,
color=FARBEN["text"], ha="left", va="center")
for spalte, wert in enumerate(ANZEIGE):
x = spalte * zellbreite
gerade_gestrichen = wert in vorher and wert not in domain
if wert in domain:
flaeche, rand, schrift = (FARBEN["gut"], FARBEN["gut"],
"white")
alpha = 0.85
elif gerade_gestrichen:
flaeche, rand, schrift = (FARBEN["fehler"],
FARBEN["fehler"], "white")
alpha = 0.75
else:
flaeche, rand, schrift = (FARBEN["flaeche"],
FARBEN["linie"],
FARBEN["linie"])
alpha = 1.0
achse.add_patch(Rectangle((x, y), zellbreite * 0.9, zellhoehe,
facecolor=flaeche, edgecolor=rand,
alpha=alpha, linewidth=1.0))
achse.text(x + zellbreite * 0.45, y + zellhoehe / 2, str(wert),
fontsize=10, color=schrift, ha="center",
va="center", fontweight="bold")
if gerade_gestrichen:
achse.plot([x + 0.1, x + zellbreite * 0.8],
[y + zellhoehe - 0.08, y + 0.08], "-",
color="white", linewidth=1.6)
anzahl = len(d1) * len(d2)
achse.text(len(ANZEIGE) * zellbreite + 0.35, oben - 0.22,
f"{len(d1)} × {len(d2)} = {anzahl}\nKombinationen",
fontsize=9.5, color=FARBEN["text"], va="center")
if geaendert is not None:
gestrichen = sorted(verlauf[stufe - 1][1 + geaendert]
- (d1, d2)[geaendert])
achse.text(len(ANZEIGE) * zellbreite + 0.35, oben - 1.15,
"gestrichen: "
+ ", ".join(str(w) for w in gestrichen),
fontsize=9, color=FARBEN["fehler"], va="center")
achse.text(-0.4, -2 * blockabstand - 1.65,
f"Bedingung $x_1 + x_2 = {SUMME}$ — kein einziger Wert wurde "
f"ausprobiert. Gestrichen wird,\nwofür es im Partner keinen "
f"Partner gibt.",
fontsize=9.5, color=FARBEN["text"], va="top")
achse.set_xlim(-0.6, len(ANZEIGE) * zellbreite + 2.6)
achse.set_ylim(-2 * blockabstand - 2.5, 1.2)
achse.set_aspect("equal")
speichere(figur, "kap_cpsat_propagation_ablauf")
if __name__ == "__main__":
verlauf = propagiere()
for titel, d1, d2, _ in verlauf:
reintitel = titel.replace("$", "").replace("\\", "")
print(f" {reintitel:<24} x1 = {sorted(d1)} x2 = {sorted(d2)} "
f"-> {len(d1) * len(d2)} Kombinationen")
anzahlen = [len(d1) * len(d2) for _, d1, d2, _ in verlauf]
if anzahlen != KOMBINATIONEN_IM_BUCH:
raise SystemExit(
f"Propagations-Ablauf: Die Propagation ergibt {anzahlen} "
f"Kombinationen, das Kapitel druckt {KOMBINATIONEN_IM_BUCH} ab.")
if (verlauf[-1][1], verlauf[-1][2]) != ENDDOMAINS_IM_BUCH:
raise SystemExit(
f"Propagations-Ablauf: Enddomains {sorted(verlauf[-1][1])} / "
f"{sorted(verlauf[-1][2])} weichen von der Handrechnung "
f"{sorted(ENDDOMAINS_IM_BUCH[0])} / "
f"{sorted(ENDDOMAINS_IM_BUCH[1])} ab.")
print(" stimmt mit der Handrechnung überein: 16 → 12 → 9")
zeichne(verlauf)

View file

@ -0,0 +1,169 @@
#!/usr/bin/env python3
# erzeuge_var_vs_cvar.py
"""
Erzeugt das Subadditivitaets-Bild zum Kapitel CVaR:
bilder_04/kap_cvar_subadditivitaet.svg
ERGAENZT kap_cvar_var_vergleich.svg, ersetzt es nicht: Jenes zeigt, WAS die
beiden Kennzahlen sind - eine Schwelle und ein Mittelwert. Dieses zeigt, warum
der Unterschied zaehlt.
Gerechnet wird das Gegenbeispiel aus Teil 2 von VaR_CVaR_Demo.py: zwei
unabhaengige Anleihen, je 100 EUR Nominal, Ausfallwahrscheinlichkeit 4 %,
sonst 2 EUR Kupon. Einzeln meldet der VaR fuer jede Anleihe einen GEWINN von
2 EUR - denn 4 % liegen unter der 5-%-Schwelle, der Ausfall faellt also aus der
Betrachtung heraus. Im Portfolio steigt die Wahrscheinlichkeit mindestens eines
Ausfalls auf 7,8 % und damit UEBER die Schwelle; der VaR springt auf 98 EUR.
Das ist die Aussage des Bildes: Der VaR behauptet, Streuung habe das Risiko um
102 EUR ERHOEHT. Der CVaR, der den ganzen Rand mittelt statt ihn abzuschneiden,
kommt auf 101,33 EUR gegenueber 159,24 EUR - Streuung senkt das Risiko, wie es
sein muss.
Die Ziehungsreihenfolge des Buchprogramms wird nachgespielt: default_rng(2026),
und Teil 1 des Programms zieht nichts, also sind die beiden Anleihen die ersten
Ziehungen. Deshalb stehen im Bild genau die Zahlen der abgedruckten Tabelle;
pruefe_gegen_buch() bricht ab, wenn nicht.
Aufruf (aus dem Repository-Wurzelverzeichnis):
python3 bilder_04/erzeuge_var_vs_cvar.py
Benoetigt: numpy, matplotlib
"""
from __future__ import annotations
import os
import sys
import numpy as np
HIER = os.path.dirname(os.path.abspath(__file__))
BASIS = os.path.dirname(HIER)
sys.path.insert(0, HIER)
sys.path.insert(0, os.path.join(
BASIS, "Operations_Research_mit_Python_Version_04_Programme"))
from stil_04 import FARBEN, speichere # noqa: E402
import matplotlib.pyplot as plt # noqa: E402
from VaR_CVaR_Demo import cvar_rockafellar, var_quantil # noqa: E402
# Instanz wortgleich zu Teil 2 von VaR_CVaR_Demo.py.
SAAT = 2026
ZIEHUNGEN = 2_000_000
AUSFALLWAHRSCHEINLICHKEIT = 0.04
VERLUST_BEI_AUSFALL = 100.0
KUPON = 2.0
# Die im Kapitel abgedruckte Tabelle; nur zum Gegenpruefen.
TABELLE_IM_BUCH = {
"Anleihe A allein": (-2.00, 79.59),
"Anleihe B allein": (-2.00, 79.66),
"Portfolio A+B": (98.00, 101.33),
"Summe der Einzelwerte": (-4.00, 159.24),
}
def rechne() -> dict[str, tuple[float, float]]:
"""Dieselben Ziehungen wie im Buchprogramm - siehe Modulkommentar."""
rng = np.random.default_rng(SAAT)
verlust_a = np.where(rng.random(ZIEHUNGEN) < AUSFALLWAHRSCHEINLICHKEIT,
VERLUST_BEI_AUSFALL, -KUPON)
verlust_b = np.where(rng.random(ZIEHUNGEN) < AUSFALLWAHRSCHEINLICHKEIT,
VERLUST_BEI_AUSFALL, -KUPON)
werte = {}
for name, v in (("Anleihe A allein", verlust_a),
("Anleihe B allein", verlust_b),
("Portfolio A+B", verlust_a + verlust_b)):
werte[name] = (var_quantil(v), cvar_rockafellar(v))
werte["Summe der Einzelwerte"] = tuple(
werte["Anleihe A allein"][i] + werte["Anleihe B allein"][i]
for i in (0, 1))
return werte
def pruefe_gegen_buch(werte) -> None:
"""Die Tafel muss dieselben Zahlen zeigen wie die Tabelle im Kapitel."""
for name, (soll_var, soll_cvar) in TABELLE_IM_BUCH.items():
ist_var, ist_cvar = werte[name]
if abs(ist_var - soll_var) > 0.005 or abs(ist_cvar - soll_cvar) > 0.005:
raise SystemExit(
f"Subadditivitaets-Bild: '{name}' ergibt VaR {ist_var:.2f} / "
f"CVaR {ist_cvar:.2f}, das Kapitel druckt {soll_var:.2f} / "
f"{soll_cvar:.2f} ab.")
# Die Aussage selbst - nicht nur die Zahlen.
if not werte["Portfolio A+B"][0] > werte["Summe der Einzelwerte"][0]:
raise SystemExit("Subadditivitaets-Bild: Der VaR verletzt die "
"Subadditivitaet hier nicht - dann zeigt die Tafel "
"das Gegenteil ihrer Überschrift.")
if not werte["Portfolio A+B"][1] <= werte["Summe der Einzelwerte"][1]:
raise SystemExit("Subadditivitaets-Bild: Der CVaR ist hier nicht "
"subadditiv. Das widerspricht dem Kapiteltext.")
def euro(wert: float) -> str:
return f"{wert:,.2f}".replace(",", "@").replace(".", ",").replace("@", ".")
def zeichne(werte) -> None:
figur, achsen = plt.subplots(1, 2, figsize=(10.2, 4.4))
namen = ["Anleihe A allein", "Anleihe B allein",
"Summe der Einzelwerte", "Portfolio A+B"]
y = np.arange(len(namen))[::-1]
for achse, index, titel, urteil, farbe in (
(achsen[0], 0, "VaR 95 %",
"Portfolio teurer als die Summe —\nSubadditivität verletzt",
FARBEN["fehler"]),
(achsen[1], 1, "CVaR 95 %",
"Portfolio billiger als die Summe —\nsubadditiv, also kohärent",
FARBEN["gut"])):
wert = [werte[n][index] for n in namen]
farben = [FARBEN["gedaempft"], FARBEN["gedaempft"],
FARBEN["zweit"], farbe]
achse.barh(y, wert, height=0.6, color=farben, zorder=3)
# Balken muessen bei null beginnen, sonst luegt ihre Laenge. Der Rand
# links richtet sich nach der Spannweite, damit die Zahl an einem
# negativen Balken nicht an der Achsenbeschriftung klebt.
spanne = max(wert) - min(0.0, min(wert))
achse.set_xlim(min(0.0, min(wert)) - 0.20 * spanne, max(wert) * 1.22)
for stelle, w in enumerate(wert):
abstand = 0.015 * spanne
achse.text(w + (abstand if w >= 0 else -abstand), y[stelle],
euro(w), va="center", ha="left" if w >= 0 else "right",
fontsize=9, color=FARBEN["text"], zorder=4)
achse.axvline(0, color=FARBEN["text"], linewidth=1.0, zorder=2)
achse.set_yticks(y)
achse.set_yticklabels(namen if index == 0 else [], fontsize=9,
color=FARBEN["text"])
achse.set_title(titel, fontsize=11, color=FARBEN["text"], pad=34)
achse.set_xlabel("Verlust in € (negativ = Gewinn)")
achse.grid(axis="y", visible=False)
# Das Urteil ueber der Achse, nicht in ihr - sonst stoesst es an die
# Zahl am obersten Balken.
achse.text(0.5, 1.008, urteil, transform=achse.transAxes,
fontsize=8.5, color=farbe, ha="center", va="bottom",
fontweight="bold")
figur.suptitle("Zwei unabhängige Anleihen, je 4 % Ausfallrisiko — "
"und zwei Urteile über dieselbe Diversifikation",
fontsize=11, color=FARBEN["text"], y=1.02)
speichere(figur, "kap_cvar_subadditivitaet")
if __name__ == "__main__":
werte = rechne()
print(f" {'':<24} {'VaR 95%':>10} {'CVaR 95%':>10}")
for name in ("Anleihe A allein", "Anleihe B allein", "Portfolio A+B",
"Summe der Einzelwerte"):
print(f" {name:<24} {werte[name][0]:>10.2f} {werte[name][1]:>10.2f}")
pruefe_gegen_buch(werte)
print(" stimmt mit der abgedruckten Tabelle überein")
print(f" VaR: Portfolio {werte['Portfolio A+B'][0]:.2f} > Summe "
f"{werte['Summe der Einzelwerte'][0]:.2f} — verletzt")
print(f" CVaR: Portfolio {werte['Portfolio A+B'][1]:.2f} ≤ Summe "
f"{werte['Summe der Einzelwerte'][1]:.2f} — kohärent")
zeichne(werte)

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 1.7 MiB

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="648pt" height="230.4pt" viewBox="0 0 648 230.4" xmlns="http://www.w3.org/2000/svg" version="1.1">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="648pt" height="244.8pt" viewBox="0 0 648 244.8" xmlns="http://www.w3.org/2000/svg" version="1.1">
<metadata>
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<cc:Work>
@ -20,8 +20,8 @@
</defs>
<g id="figure_1">
<g id="patch_1">
<path d="M 0 230.4
L 648 230.4
<path d="M 0 244.8
L 648 244.8
L 648 0
L 0 0
z
@ -29,83 +29,83 @@ z
</g>
<g id="axes_1">
<g id="patch_2">
<path d="M 72.79 188.44
L 630.81 188.44
<path d="M 72.79 202.84
L 630.81 202.84
L 630.81 25.44
L 72.79 25.44
z
" style="fill: #ffffff"/>
</g>
<g id="patch_3">
<path d="M 174.248182 32.849091
L 326.435455 32.849091
L 326.435455 64.809875
L 174.248182 64.809875
<path d="M 174.248182 33.503636
L 326.435455 33.503636
L 326.435455 68.28795
L 174.248182 68.28795
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_4">
<path d="M 326.435455 90.959608
L 427.893636 90.959608
L 427.893636 122.920392
L 326.435455 122.920392
<path d="M 326.435455 96.747843
L 427.893636 96.747843
L 427.893636 131.532157
L 326.435455 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_5">
<path d="M 427.893636 149.070125
L 529.351818 149.070125
L 529.351818 181.030909
L 427.893636 181.030909
<path d="M 427.893636 159.99205
L 529.351818 159.99205
L 529.351818 194.776364
L 427.893636 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #4338ca; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_6">
<path d="M 72.79 32.849091
L 174.248182 32.849091
L 174.248182 64.809875
L 72.79 64.809875
<path d="M 72.79 33.503636
L 174.248182 33.503636
L 174.248182 68.28795
L 72.79 68.28795
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_7">
<path d="M 174.248182 149.070125
L 224.977273 149.070125
L 224.977273 181.030909
L 174.248182 181.030909
<path d="M 174.248182 159.99205
L 224.977273 159.99205
L 224.977273 194.776364
L 174.248182 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_8">
<path d="M 427.893636 90.959608
L 630.81 90.959608
L 630.81 122.920392
L 427.893636 122.920392
<path d="M 427.893636 96.747843
L 630.81 96.747843
L 630.81 131.532157
L 427.893636 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #0891b2; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_9">
<path d="M 72.79 90.959608
L 275.706364 90.959608
L 275.706364 122.920392
L 72.79 122.920392
<path d="M 72.79 96.747843
L 275.706364 96.747843
L 275.706364 131.532157
L 72.79 131.532157
z
" clip-path="url(#pce73ba693e)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_10">
<path d="M 275.706364 149.070125
L 427.893636 149.070125
L 427.893636 181.030909
L 275.706364 181.030909
<path d="M 275.706364 159.99205
L 427.893636 159.99205
L 427.893636 194.776364
L 275.706364 194.776364
z
" clip-path="url(#pce73ba693e)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
" clip-path="url(#p3043647a47)" style="fill: #b45309; stroke: #ffffff; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="matplotlib.axis_1">
<g id="xtick_1">
<g id="line2d_1">
<path d="M 72.79 188.44
<path d="M 72.79 202.84
L 72.79 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_2">
<defs>
@ -114,12 +114,12 @@ L 0 3.5
" style="stroke: #000000; stroke-width: 0.8"/>
</defs>
<g>
<use xlink:href="#mf0a04b41f8" x="72.79" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="72.79" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_1">
<!-- 0 -->
<g transform="translate(69.60875 203.038437) scale(0.1 -0.1)">
<g transform="translate(69.60875 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-30" d="M 2034 4250
Q 1547 4250 1301 3770
@ -149,18 +149,18 @@ z
</g>
<g id="xtick_2">
<g id="line2d_3">
<path d="M 123.519091 188.44
<path d="M 123.519091 202.84
L 123.519091 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_4">
<g>
<use xlink:href="#mf0a04b41f8" x="123.519091" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="123.519091" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_2">
<!-- 1 -->
<g transform="translate(120.337841 203.038437) scale(0.1 -0.1)">
<g transform="translate(120.337841 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-31" d="M 794 531
L 1825 531
@ -183,18 +183,18 @@ z
</g>
<g id="xtick_3">
<g id="line2d_5">
<path d="M 174.248182 188.44
<path d="M 174.248182 202.84
L 174.248182 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_6">
<g>
<use xlink:href="#mf0a04b41f8" x="174.248182" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="174.248182" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_3">
<!-- 2 -->
<g transform="translate(171.066932 203.038437) scale(0.1 -0.1)">
<g transform="translate(171.066932 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-32" d="M 1228 531
L 3431 531
@ -227,18 +227,18 @@ z
</g>
<g id="xtick_4">
<g id="line2d_7">
<path d="M 224.977273 188.44
<path d="M 224.977273 202.84
L 224.977273 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_8">
<g>
<use xlink:href="#mf0a04b41f8" x="224.977273" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="224.977273" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_4">
<!-- 3 -->
<g transform="translate(221.796023 203.038437) scale(0.1 -0.1)">
<g transform="translate(221.796023 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-33" d="M 2597 2516
Q 3050 2419 3304 2112
@ -279,18 +279,18 @@ z
</g>
<g id="xtick_5">
<g id="line2d_9">
<path d="M 275.706364 188.44
<path d="M 275.706364 202.84
L 275.706364 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_10">
<g>
<use xlink:href="#mf0a04b41f8" x="275.706364" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="275.706364" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_5">
<!-- 4 -->
<g transform="translate(272.525114 203.038437) scale(0.1 -0.1)">
<g transform="translate(272.525114 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-34" d="M 2419 4116
L 825 1625
@ -318,18 +318,18 @@ z
</g>
<g id="xtick_6">
<g id="line2d_11">
<path d="M 326.435455 188.44
<path d="M 326.435455 202.84
L 326.435455 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_12">
<g>
<use xlink:href="#mf0a04b41f8" x="326.435455" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="326.435455" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_6">
<!-- 5 -->
<g transform="translate(323.254205 203.038437) scale(0.1 -0.1)">
<g transform="translate(323.254205 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-35" d="M 691 4666
L 3169 4666
@ -363,18 +363,18 @@ z
</g>
<g id="xtick_7">
<g id="line2d_13">
<path d="M 377.164545 188.44
<path d="M 377.164545 202.84
L 377.164545 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_14">
<g>
<use xlink:href="#mf0a04b41f8" x="377.164545" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="377.164545" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_7">
<!-- 6 -->
<g transform="translate(373.983295 203.038437) scale(0.1 -0.1)">
<g transform="translate(373.983295 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-36" d="M 2113 2584
Q 1688 2584 1439 2293
@ -413,18 +413,18 @@ z
</g>
<g id="xtick_8">
<g id="line2d_15">
<path d="M 427.893636 188.44
<path d="M 427.893636 202.84
L 427.893636 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_16">
<g>
<use xlink:href="#mf0a04b41f8" x="427.893636" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="427.893636" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_8">
<!-- 7 -->
<g transform="translate(424.712386 203.038437) scale(0.1 -0.1)">
<g transform="translate(424.712386 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-37" d="M 525 4666
L 3525 4666
@ -443,18 +443,18 @@ z
</g>
<g id="xtick_9">
<g id="line2d_17">
<path d="M 478.622727 188.44
<path d="M 478.622727 202.84
L 478.622727 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_18">
<g>
<use xlink:href="#mf0a04b41f8" x="478.622727" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="478.622727" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_9">
<!-- 8 -->
<g transform="translate(475.441477 203.038437) scale(0.1 -0.1)">
<g transform="translate(475.441477 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-38" d="M 2034 2216
Q 1584 2216 1326 1975
@ -502,18 +502,18 @@ z
</g>
<g id="xtick_10">
<g id="line2d_19">
<path d="M 529.351818 188.44
<path d="M 529.351818 202.84
L 529.351818 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_20">
<g>
<use xlink:href="#mf0a04b41f8" x="529.351818" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="529.351818" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_10">
<!-- 9 -->
<g transform="translate(526.170568 203.038437) scale(0.1 -0.1)">
<g transform="translate(526.170568 217.438437) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-39" d="M 703 97
L 703 672
@ -552,18 +552,18 @@ z
</g>
<g id="xtick_11">
<g id="line2d_21">
<path d="M 580.080909 188.44
<path d="M 580.080909 202.84
L 580.080909 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_22">
<g>
<use xlink:href="#mf0a04b41f8" x="580.080909" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="580.080909" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_11">
<!-- 10 -->
<g transform="translate(573.718409 203.038437) scale(0.1 -0.1)">
<g transform="translate(573.718409 217.438437) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
</g>
@ -571,18 +571,18 @@ L 580.080909 25.44
</g>
<g id="xtick_12">
<g id="line2d_23">
<path d="M 630.81 188.44
<path d="M 630.81 202.84
L 630.81 25.44
" clip-path="url(#pce73ba693e)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_24">
<g>
<use xlink:href="#mf0a04b41f8" x="630.81" y="188.44" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mf0a04b41f8" x="630.81" y="202.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_12">
<!-- 11 -->
<g transform="translate(624.4475 203.038437) scale(0.1 -0.1)">
<g transform="translate(624.4475 217.438437) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use xlink:href="#DejaVuSans-31" transform="translate(63.623047 0)"/>
</g>
@ -590,7 +590,7 @@ L 630.81 25.44
</g>
<g id="text_13">
<!-- Zeiteinheiten -->
<g transform="translate(318.475 216.716562) scale(0.1 -0.1)">
<g transform="translate(318.475 231.116562) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-5a" d="M 359 4666
L 4025 4666
@ -728,12 +728,12 @@ L -3.5 0
" style="stroke: #000000; stroke-width: 0.8"/>
</defs>
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="48.829483" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="50.895793" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_14">
<!-- Fraese -->
<g transform="translate(33.00875 52.628702) scale(0.1 -0.1)">
<g transform="translate(33.00875 54.695012) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-46" d="M 628 4666
L 3309 4666
@ -842,12 +842,12 @@ z
<g id="ytick_2">
<g id="line2d_26">
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="106.94" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="114.14" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_15">
<!-- Dreherei -->
<g transform="translate(22.743125 110.739219) scale(0.1 -0.1)">
<g transform="translate(22.743125 117.939219) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-44" d="M 1259 4147
L 1259 519
@ -883,12 +883,12 @@ z
<g id="ytick_3">
<g id="line2d_27">
<g>
<use xlink:href="#mc0b9ff744f" x="72.79" y="165.050517" style="stroke: #000000; stroke-width: 0.8"/>
<use xlink:href="#mc0b9ff744f" x="72.79" y="177.384207" style="stroke: #000000; stroke-width: 0.8"/>
</g>
</g>
<g id="text_16">
<!-- Lackiererei -->
<g transform="translate(11.013438 168.849736) scale(0.1 -0.1)">
<g transform="translate(11.013438 181.183426) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-4c" d="M 628 4666
L 1259 4666
@ -951,13 +951,29 @@ z
</g>
</g>
<g id="patch_11">
<path d="M 72.79 188.44
L 630.81 188.44
<path d="M 275.706364 96.747843
L 326.435455 96.747843
L 326.435455 131.532157
L 275.706364 131.532157
z
" clip-path="url(#p3043647a47)" style="fill: url(#h8ac0198506); stroke: #be123c; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_12">
<path d="M 224.977273 159.99205
L 275.706364 159.99205
L 275.706364 194.776364
L 224.977273 194.776364
z
" clip-path="url(#p3043647a47)" style="fill: url(#h8ac0198506); stroke: #be123c; stroke-width: 1.2; stroke-linejoin: miter"/>
</g>
<g id="patch_13">
<path d="M 72.79 202.84
L 630.81 202.84
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
</g>
<g id="text_17">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(242.993381 51.588858) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(242.993381 53.655168) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-41" d="M 3419 850
L 1538 850
@ -1003,21 +1019,21 @@ z
</g>
<g id="text_18">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(369.816108 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(369.816108 116.899375) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-30" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_19">
<!-- A0 -->
<g style="fill: #ffffff" transform="translate(471.27429 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(471.27429 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-30" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_20">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(116.170653 51.588858) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(116.170653 53.655168) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-31" d="M 750 831
L 1813 831
@ -1040,21 +1056,21 @@ z
</g>
<g id="text_21">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(192.26429 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(192.26429 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-31" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_22">
<!-- A1 -->
<g style="fill: #ffffff" transform="translate(522.003381 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(522.003381 116.899375) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-31" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_23">
<!-- A2 -->
<g style="fill: #ffffff" transform="translate(166.899744 109.699375) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(166.899744 116.899375) scale(0.1 -0.1)">
<defs>
<path id="DejaVuSans-Bold-32" d="M 1844 884
L 3897 884
@ -1085,14 +1101,14 @@ z
</g>
<g id="text_24">
<!-- A2 -->
<g style="fill: #ffffff" transform="translate(344.451563 167.809892) scale(0.1 -0.1)">
<g style="fill: #ffffff" transform="translate(344.451563 180.143582) scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-Bold-41"/>
<use xlink:href="#DejaVuSans-Bold-32" transform="translate(77.392578 0)"/>
</g>
</g>
<g id="text_25">
<!-- Job-Shop-Plan, Makespan = 11 -->
<g transform="translate(267.519375 19.44) scale(0.11 -0.11)">
<!-- Job-Shop-Plan: Makespan 11, untere Schranke 10 — schraffiert steht die Maschine still -->
<g transform="translate(114.118359 19.44) scale(0.11 -0.11)">
<defs>
<path id="DejaVuSans-4a" d="M 628 4666
L 1259 4666
@ -1246,14 +1262,18 @@ L 603 0
L 603 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2c" d="M 750 794
<path id="DejaVuSans-3a" d="M 750 794
L 1409 794
L 1409 256
L 897 -744
L 494 -744
L 750 256
L 1409 0
L 750 0
L 750 794
z
M 750 3309
L 1409 3309
L 1409 2516
L 750 2516
L 750 3309
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-20" transform="scale(0.015625)"/>
<path id="DejaVuSans-4d" d="M 628 4666
@ -1272,17 +1292,89 @@ L 628 0
L 628 4666
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-3d" d="M 678 2906
L 4684 2906
L 4684 2381
L 678 2381
L 678 2906
<path id="DejaVuSans-2c" d="M 750 794
L 1409 794
L 1409 256
L 897 -744
L 494 -744
L 750 256
L 750 794
z
M 678 1631
L 4684 1631
L 4684 1100
L 678 1100
L 678 1631
" transform="scale(0.015625)"/>
<path id="DejaVuSans-75" d="M 544 1381
L 544 3500
L 1119 3500
L 1119 1403
Q 1119 906 1312 657
Q 1506 409 1894 409
Q 2359 409 2629 706
Q 2900 1003 2900 1516
L 2900 3500
L 3475 3500
L 3475 0
L 2900 0
L 2900 538
Q 2691 219 2414 64
Q 2138 -91 1772 -91
Q 1169 -91 856 284
Q 544 659 544 1381
z
M 1991 3584
L 1991 3584
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2014" d="M 313 1978
L 6088 1978
L 6088 1528
L 313 1528
L 313 1978
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-66" d="M 2375 4863
L 2375 4384
L 1825 4384
Q 1516 4384 1395 4259
Q 1275 4134 1275 3809
L 1275 3500
L 2222 3500
L 2222 3053
L 1275 3053
L 1275 0
L 697 0
L 697 3053
L 147 3053
L 147 3500
L 697 3500
L 697 3744
Q 697 4328 969 4595
Q 1241 4863 1831 4863
L 2375 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-64" d="M 2906 2969
L 2906 4863
L 3481 4863
L 3481 0
L 2906 0
L 2906 525
Q 2725 213 2448 61
Q 2172 -91 1784 -91
Q 1150 -91 751 415
Q 353 922 353 1747
Q 353 2572 751 3078
Q 1150 3584 1784 3584
Q 2172 3584 2448 3432
Q 2725 3281 2906 2969
z
M 947 1747
Q 947 1113 1208 752
Q 1469 391 1925 391
Q 2381 391 2643 752
Q 2906 1113 2906 1747
Q 2906 2381 2643 2742
Q 2381 3103 1925 3103
Q 1469 3103 1208 2742
Q 947 2381 947 1747
z
" transform="scale(0.015625)"/>
</defs>
@ -1299,28 +1391,157 @@ z
<use xlink:href="#DejaVuSans-6c" transform="translate(538.134766 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(565.917969 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(627.197266 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(690.576172 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(722.363281 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(754.150391 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(840.429688 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(901.708984 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(955.994141 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(1017.517578 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(1069.617188 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(1133.09375 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1194.373047 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1257.751953 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(1289.539062 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1373.328125 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1405.115234 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1468.738281 0)"/>
<use xlink:href="#DejaVuSans-3a" transform="translate(690.576172 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(724.267578 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(756.054688 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(842.333984 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(903.613281 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(957.898438 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(1019.421875 0)"/>
<use xlink:href="#DejaVuSans-70" transform="translate(1071.521484 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(1134.998047 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1196.277344 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1259.65625 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1291.443359 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1355.066406 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(1418.689453 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1450.476562 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1482.263672 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1545.642578 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1609.021484 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1648.230469 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(1709.753906 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1748.617188 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1810.140625 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(1841.927734 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(1905.404297 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1960.384766 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2023.763672 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(2064.876953 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(2126.15625 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(2189.535156 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2243.820312 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2305.34375 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(2337.130859 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(2400.753906 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2464.376953 0)"/>
<use xlink:href="#DejaVuSans-2014" transform="translate(2496.164062 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(2596.164062 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(2627.951172 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(2680.050781 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(2735.03125 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2798.410156 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(2839.523438 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(2900.802734 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(2936.007812 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(2971.212891 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2998.996094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(3060.519531 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3101.632812 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3140.841797 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(3172.628906 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3224.728516 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(3263.9375 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(3325.460938 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(3388.839844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3428.048828 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(3459.835938 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(3523.3125 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(3551.095703 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(3612.619141 0)"/>
<use xlink:href="#DejaVuSans-4d" transform="translate(3644.40625 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(3730.685547 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(3791.964844 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(3844.064453 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(3899.044922 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(3962.423828 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(3990.207031 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(4053.585938 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(4115.109375 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(4146.896484 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(4198.996094 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(4238.205078 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(4265.988281 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(4293.771484 0)"/>
</g>
</g>
<g id="line2d_28">
<path d="M 580.080909 202.84
L 580.080909 25.44
" clip-path="url(#p3043647a47)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #059669; stroke-width: 1.5"/>
</g>
<g id="text_26">
<!-- untere Schranke 10 -->
<g style="fill: #059669" transform="translate(490.514121 195.750266) scale(0.085 -0.085)">
<use xlink:href="#DejaVuSans-75"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(63.378906 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(126.757812 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(165.966797 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(227.490234 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(266.353516 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(327.876953 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(359.664062 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(423.140625 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(478.121094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(541.5 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(582.613281 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(643.892578 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(707.271484 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(761.556641 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(823.080078 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(854.867188 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(918.490234 0)"/>
</g>
</g>
</g>
</g>
<defs>
<clipPath id="pce73ba693e">
<rect x="72.79" y="25.44" width="558.02" height="163"/>
<clipPath id="p3043647a47">
<rect x="72.79" y="25.44" width="558.02" height="177.4"/>
</clipPath>
</defs>
<defs>
<pattern id="h8ac0198506" patternUnits="userSpaceOnUse" x="0" y="0" width="72" height="72">
<rect x="0" y="0" width="73" height="73" fill="none"/>
<path d="M -36 36
L 36 -36
M -32 40
L 40 -32
M -28 44
L 44 -28
M -24 48
L 48 -24
M -20 52
L 52 -20
M -16 56
L 56 -16
M -12 60
L 60 -12
M -8 64
L 64 -8
M -4 68
L 68 -4
M 0 72
L 72 0
M 4 76
L 76 4
M 8 80
L 80 8
M 12 84
L 84 12
M 16 88
L 88 16
M 20 92
L 92 20
M 24 96
L 96 24
M 28 100
L 100 28
M 32 104
L 104 32
M 36 108
L 108 36
" style="fill: #be123c; stroke: #be123c; stroke-width: 1.0; stroke-linecap: butt; stroke-linejoin: miter"/>
</pattern>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 103 KiB

View file

@ -749,6 +749,260 @@ L 492.615285 242.66828
<path d="M 380.229919 330.28
L 380.229919 37.968
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 4.81,2.08; stroke-dashoffset: 0; stroke: #b45309; stroke-opacity: 0.8; stroke-width: 1.3"/>
</g>
<g id="line2d_29">
<path d="M 46.82 294.907792
L 51.474343 296.738929
L 56.128687 298.570066
L 60.78303 300.401203
L 65.437374 302.23234
L 70.091717 304.063477
L 74.746061 305.894614
L 79.400404 307.725751
L 84.054747 309.556888
L 88.709091 311.388025
L 93.363434 313.219162
L 98.017778 315.050299
L 102.672121 316.881436
L 107.326465 318.712573
L 111.980808 320.54371
L 116.635152 322.374847
L 121.289495 324.205984
L 125.943838 326.037121
L 130.598182 327.868259
L 135.252525 329.699396
L 139.906869 331.530533
L 144.561212 333.36167
L 149.215556 335.192807
L 153.869899 337.023944
L 158.524242 338.855081
L 163.178586 340.686218
L 167.832929 342.517355
L 172.487273 344.348492
L 177.141616 346.179629
L 181.79596 348.010766
L 186.450303 349.841903
L 191.104646 351.67304
L 195.75899 353.504177
L 200.413333 355.335314
L 205.067677 357.166451
L 209.72202 358.997588
L 214.376364 360.828725
L 219.030707 362.659862
L 223.685051 364.491
L 228.339394 366.322137
L 232.993737 368.153274
L 237.648081 369.984411
L 242.302424 371.815548
L 246.956768 373.646685
L 251.413305 375.4
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="line2d_30">
<path d="M 46.82 98.395523
L 51.474343 100.22666
L 56.128687 102.057797
L 60.78303 103.888934
L 65.437374 105.720071
L 70.091717 107.551208
L 74.746061 109.382345
L 79.400404 111.213482
L 84.054747 113.044619
L 88.709091 114.875756
L 93.363434 116.706893
L 98.017778 118.53803
L 102.672121 120.369167
L 107.326465 122.200304
L 111.980808 124.031441
L 116.635152 125.862578
L 121.289495 127.693716
L 125.943838 129.524853
L 130.598182 131.35599
L 135.252525 133.187127
L 139.906869 135.018264
L 144.561212 136.849401
L 149.215556 138.680538
L 153.869899 140.511675
L 158.524242 142.342812
L 163.178586 144.173949
L 167.832929 146.005086
L 172.487273 147.836223
L 177.141616 149.66736
L 181.79596 151.498497
L 186.450303 153.329634
L 191.104646 155.160771
L 195.75899 156.991908
L 200.413333 158.823045
L 205.067677 160.654182
L 209.72202 162.485319
L 214.376364 164.316457
L 219.030707 166.147594
L 223.685051 167.978731
L 228.339394 169.809868
L 232.993737 171.641005
L 237.648081 173.472142
L 242.302424 175.303279
L 246.956768 177.134416
L 251.611111 178.965553
L 256.265455 180.79669
L 260.919798 182.627827
L 265.574141 184.458964
L 270.228485 186.290101
L 274.882828 188.121238
L 279.537172 189.952375
L 284.191515 191.783512
L 288.845859 193.614649
L 293.500202 195.445786
L 298.154545 197.276923
L 302.808889 199.108061
L 307.463232 200.939198
L 312.117576 202.770335
L 316.771919 204.601472
L 321.426263 206.432609
L 326.080606 208.263746
L 330.734949 210.094883
L 335.389293 211.92602
L 340.043636 213.757157
L 344.69798 215.588294
L 349.352323 217.419431
L 354.006667 219.250568
L 358.66101 221.081705
L 363.315354 222.912842
L 367.969697 224.743979
L 372.62404 226.575116
L 377.278384 228.406253
L 381.932727 230.23739
L 386.587071 232.068527
L 391.241414 233.899664
L 395.895758 235.730802
L 400.550101 237.561939
L 405.204444 239.393076
L 409.858788 241.224213
L 414.513131 243.05535
L 419.167475 244.886487
L 423.821818 246.717624
L 428.476162 248.548761
L 433.130505 250.379898
L 437.784848 252.211035
L 442.439192 254.042172
L 447.093535 255.873309
L 451.747879 257.704446
L 456.402222 259.535583
L 461.056566 261.36672
L 465.710909 263.197857
L 470.365253 265.028994
L 475.019596 266.860131
L 479.673939 268.691268
L 484.328283 270.522406
L 488.982626 272.353543
L 493.63697 274.18468
L 498.291313 276.015817
L 502.945657 277.846954
L 507.6 279.678091
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="line2d_31">
<path d="M 46.82 61.549472
L 51.474343 63.380609
L 56.128687 65.211746
L 60.78303 67.042883
L 65.437374 68.87402
L 70.091717 70.705158
L 74.746061 72.536295
L 79.400404 74.367432
L 84.054747 76.198569
L 88.709091 78.029706
L 93.363434 79.860843
L 98.017778 81.69198
L 102.672121 83.523117
L 107.326465 85.354254
L 111.980808 87.185391
L 116.635152 89.016528
L 121.289495 90.847665
L 125.943838 92.678802
L 130.598182 94.509939
L 135.252525 96.341076
L 139.906869 98.172213
L 144.561212 100.00335
L 149.215556 101.834487
L 153.869899 103.665624
L 158.524242 105.496761
L 163.178586 107.327899
L 167.832929 109.159036
L 172.487273 110.990173
L 177.141616 112.82131
L 181.79596 114.652447
L 186.450303 116.483584
L 191.104646 118.314721
L 195.75899 120.145858
L 200.413333 121.976995
L 205.067677 123.808132
L 209.72202 125.639269
L 214.376364 127.470406
L 219.030707 129.301543
L 223.685051 131.13268
L 228.339394 132.963817
L 232.993737 134.794954
L 237.648081 136.626091
L 242.302424 138.457228
L 246.956768 140.288365
L 251.611111 142.119503
L 256.265455 143.95064
L 260.919798 145.781777
L 265.574141 147.612914
L 270.228485 149.444051
L 274.882828 151.275188
L 279.537172 153.106325
L 284.191515 154.937462
L 288.845859 156.768599
L 293.500202 158.599736
L 298.154545 160.430873
L 302.808889 162.26201
L 307.463232 164.093147
L 312.117576 165.924284
L 316.771919 167.755421
L 321.426263 169.586558
L 326.080606 171.417695
L 330.734949 173.248832
L 335.389293 175.079969
L 340.043636 176.911106
L 344.69798 178.742244
L 349.352323 180.573381
L 354.006667 182.404518
L 358.66101 184.235655
L 363.315354 186.066792
L 367.969697 187.897929
L 372.62404 189.729066
L 377.278384 191.560203
L 381.932727 193.39134
L 386.587071 195.222477
L 391.241414 197.053614
L 395.895758 198.884751
L 400.550101 200.715888
L 405.204444 202.547025
L 409.858788 204.378162
L 414.513131 206.209299
L 419.167475 208.040436
L 423.821818 209.871573
L 428.476162 211.70271
L 433.130505 213.533848
L 437.784848 215.364985
L 442.439192 217.196122
L 447.093535 219.027259
L 451.747879 220.858396
L 456.402222 222.689533
L 461.056566 224.52067
L 465.710909 226.351807
L 470.365253 228.182944
L 475.019596 230.014081
L 479.673939 231.845218
L 484.328283 233.676355
L 488.982626 235.507492
L 493.63697 237.338629
L 498.291313 239.169766
L 502.945657 241.000903
L 507.6 242.83204
" clip-path="url(#pe55ef05f0d)" style="fill: none; stroke-dasharray: 1.3,2.145; stroke-dashoffset: 0; stroke: #7c3aed; stroke-opacity: 0.9; stroke-width: 1.3"/>
</g>
<g id="patch_4">
<path d="M 46.82 330.28
@ -1728,7 +1982,7 @@ z
<use xlink:href="#DejaVuSans-6c" transform="translate(1244.861328 0)"/>
</g>
</g>
<g id="line2d_29">
<g id="line2d_32">
<path d="M 80.53561 308.17237
L 80.53561 111.660101
L 361.499024 185.352202
@ -1776,7 +2030,7 @@ z
</g>
</g>
<g id="legend_1">
<g id="line2d_30">
<g id="line2d_33">
<path d="M 394.72 48.44675
L 402.72 48.44675
L 410.72 48.44675
@ -1908,7 +2162,7 @@ z
<use xlink:href="#DejaVuSans-30" transform="translate(977.255859 0.78125)"/>
</g>
</g>
<g id="line2d_31">
<g id="line2d_34">
<path d="M 394.72 60.18925
L 402.72 60.18925
L 410.72 60.18925
@ -1980,7 +2234,7 @@ z
<use xlink:href="#DejaVuSans-30" transform="translate(938.925781 0.78125)"/>
</g>
</g>
<g id="line2d_32">
<g id="line2d_35">
<path d="M 394.72 71.93175
L 402.72 71.93175
L 410.72 71.93175
@ -2008,6 +2262,177 @@ L 410.72 71.93175
</g>
</g>
</g>
<g id="text_24">
<!-- Z = 0 € -->
<g style="fill: #7c3aed" transform="translate(130.162252 327.737673) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-5a" d="M 359 4666
L 4025 4666
L 4025 4184
L 1075 531
L 4097 531
L 4097 0
L 288 0
L 288 481
L 3238 4134
L 359 4134
L 359 4666
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-3d" d="M 678 2906
L 4684 2906
L 4684 2381
L 678 2381
L 678 2906
z
M 678 1631
L 4684 1631
L 4684 1100
L 678 1100
L 678 1631
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-20ac" d="M 3647 4306
L 3647 3641
Q 3363 3969 3098 4109
Q 2834 4250 2516 4250
Q 2025 4250 1709 3937
Q 1394 3625 1294 3041
L 3097 3041
L 2925 2656
L 1244 2656
Q 1238 2581 1236 2509
Q 1234 2438 1234 2328
Q 1234 2228 1236 2156
Q 1238 2084 1244 2009
L 2638 2009
L 2463 1625
L 1294 1625
Q 1394 1041 1709 725
Q 2025 409 2516 409
Q 2834 409 3098 550
Q 3363 691 3647 1019
L 3647 359
Q 3369 134 3080 21
Q 2791 -91 2491 -91
Q 1750 -91 1267 362
Q 784 816 659 1625
L 0 1625
L 172 2009
L 606 2009
Q 606 2081 604 2154
Q 603 2228 603 2328
Q 603 2438 604 2511
Q 606 2584 606 2656
L 0 2656
L 172 3041
L 659 3041
Q 784 3844 1268 4297
Q 1753 4750 2491 4750
Q 2797 4750 3086 4639
Q 3375 4528 3647 4306
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(311.279297 0)"/>
</g>
</g>
<g id="text_25">
<!-- Z = 2.000 € -->
<g style="fill: #7c3aed" transform="translate(436.491667 273.094902) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-2e" d="M 684 794
L 1344 794
L 1344 0
L 684 0
L 684 794
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(311.279297 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(374.902344 0)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(438.525391 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(502.148438 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(533.935547 0)"/>
</g>
</g>
<g id="text_26">
<!-- Z = 2.375 € -->
<g style="fill: #7c3aed" transform="translate(436.491667 236.248852) scale(0.075 -0.075)">
<use xlink:href="#DejaVuSans-5a"/>
<use xlink:href="#DejaVuSans-20" transform="translate(68.505859 0)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(100.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(184.082031 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(215.869141 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(279.492188 0)"/>
<use xlink:href="#DejaVuSans-33" transform="translate(311.279297 0)"/>
<use xlink:href="#DejaVuSans-37" transform="translate(374.902344 0)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(438.525391 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(502.148438 0)"/>
<use xlink:href="#DejaVuSans-20ac" transform="translate(533.935547 0)"/>
</g>
</g>
<g id="patch_6">
<path d="M 179.285106 252.657412
Q 202.992666 226.748642 225.039768 202.654502
" style="fill: none; stroke: #7c3aed; stroke-width: 2.2; stroke-linecap: round"/>
<path d="M 220.863976 204.255364
L 225.039768 202.654502
L 223.814983 206.955653
z
" style="fill: #7c3aed; stroke: #7c3aed; stroke-width: 2.2; stroke-linecap: round"/>
</g>
<g id="text_27">
<!-- $\mathbf{c} = (150; 250)$ -->
<g style="fill: #7c3aed" transform="translate(233.66775 201.711903) scale(0.085 -0.085)">
<defs>
<path id="DejaVuSans-Bold-63" d="M 3366 3391
L 3366 2478
Q 3138 2634 2908 2709
Q 2678 2784 2431 2784
Q 1963 2784 1702 2511
Q 1441 2238 1441 1747
Q 1441 1256 1702 982
Q 1963 709 2431 709
Q 2694 709 2930 787
Q 3166 866 3366 1019
L 3366 103
Q 3103 6 2833 -42
Q 2563 -91 2291 -91
Q 1344 -91 809 395
Q 275 881 275 1747
Q 275 2613 809 3098
Q 1344 3584 2291 3584
Q 2566 3584 2833 3536
Q 3100 3488 3366 3391
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-Bold-63" transform="translate(0 0.125)"/>
<use xlink:href="#DejaVuSans-3d" transform="translate(78.759766 0.125)"/>
<use xlink:href="#DejaVuSans-28" transform="translate(182.03125 0.125)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(221.044922 0.125)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(284.667969 0.125)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(348.291016 0.125)"/>
<use xlink:href="#DejaVuSans-3b" transform="translate(411.914062 0.125)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(465.087891 0.125)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(528.710938 0.125)"/>
<use xlink:href="#DejaVuSans-30" transform="translate(592.333984 0.125)"/>
<use xlink:href="#DejaVuSans-29" transform="translate(655.957031 0.125)"/>
</g>
</g>
</g>
</g>
<defs>

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

View file

@ -44,15 +44,25 @@ L 275.546558 234.022071
L 399.362509 260.929849
L 469.719463 263.503636
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 5.18,2.24; stroke-dashoffset: 0; stroke: #4338ca; stroke-width: 1.4"/>
</g>
<g id="line2d_2">
<path d="M 57.44 85.651402
L 363.250105 317.8
" clip-path="url(#pc5415b3683)" style="fill: none; stroke: #4338ca; stroke-opacity: 0.75; stroke-width: 1.1; stroke-linecap: square"/>
</g>
<g id="line2d_3">
<path d="M 57.44 168.507483
L 489.351818 168.507483
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 4.44,1.92; stroke-dashoffset: 0; stroke: #059669; stroke-opacity: 0.9; stroke-width: 1.2"/>
</g>
<g id="matplotlib.axis_1">
<g id="xtick_1">
<g id="line2d_2">
<g id="line2d_4">
<path d="M 94.584785 274.84
L 94.584785 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_3">
<g id="line2d_5">
<defs>
<path id="mf0a04b41f8" d="M 0 0
L 0 3.5
@ -173,12 +183,12 @@ z
</g>
</g>
<g id="xtick_2">
<g id="line2d_4">
<g id="line2d_6">
<path d="M 156.031907 274.84
L 156.031907 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_5">
<g id="line2d_7">
<g>
<use xlink:href="#mf0a04b41f8" x="156.031907" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -236,12 +246,12 @@ z
</g>
</g>
<g id="xtick_3">
<g id="line2d_6">
<g id="line2d_8">
<path d="M 217.479028 274.84
L 217.479028 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_7">
<g id="line2d_9">
<g>
<use xlink:href="#mf0a04b41f8" x="217.479028" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -279,12 +289,12 @@ z
</g>
</g>
<g id="xtick_4">
<g id="line2d_8">
<g id="line2d_10">
<path d="M 278.92615 274.84
L 278.92615 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_9">
<g id="line2d_11">
<g>
<use xlink:href="#mf0a04b41f8" x="278.92615" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -327,12 +337,12 @@ z
</g>
</g>
<g id="xtick_5">
<g id="line2d_10">
<g id="line2d_12">
<path d="M 340.373272 274.84
L 340.373272 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_11">
<g id="line2d_13">
<g>
<use xlink:href="#mf0a04b41f8" x="340.373272" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -349,12 +359,12 @@ L 340.373272 25.44
</g>
</g>
<g id="xtick_6">
<g id="line2d_12">
<g id="line2d_14">
<path d="M 401.820393 274.84
L 401.820393 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_13">
<g id="line2d_15">
<g>
<use xlink:href="#mf0a04b41f8" x="401.820393" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -371,12 +381,12 @@ L 401.820393 25.44
</g>
</g>
<g id="xtick_7">
<g id="line2d_14">
<g id="line2d_16">
<path d="M 463.267515 274.84
L 463.267515 25.44
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_15">
<g id="line2d_17">
<g>
<use xlink:href="#mf0a04b41f8" x="463.267515" y="274.84" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -728,12 +738,12 @@ z
</g>
<g id="matplotlib.axis_2">
<g id="ytick_1">
<g id="line2d_16">
<g id="line2d_18">
<path d="M 57.44 241.509453
L 489.351818 241.509453
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_17">
<g id="line2d_19">
<defs>
<path id="mc0b9ff744f" d="M 0 0
L -3.5 0
@ -781,12 +791,12 @@ z
</g>
</g>
<g id="ytick_2">
<g id="line2d_18">
<g id="line2d_20">
<path d="M 57.44 194.713318
L 489.351818 194.713318
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_19">
<g id="line2d_21">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="194.713318" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -802,12 +812,12 @@ L 489.351818 194.713318
</g>
</g>
<g id="ytick_3">
<g id="line2d_20">
<g id="line2d_22">
<path d="M 57.44 147.917184
L 489.351818 147.917184
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_21">
<g id="line2d_23">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="147.917184" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -823,12 +833,12 @@ L 489.351818 147.917184
</g>
</g>
<g id="ytick_4">
<g id="line2d_22">
<g id="line2d_24">
<path d="M 57.44 101.121049
L 489.351818 101.121049
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_23">
<g id="line2d_25">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="101.121049" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -844,12 +854,12 @@ L 489.351818 101.121049
</g>
</g>
<g id="ytick_5">
<g id="line2d_24">
<g id="line2d_26">
<path d="M 57.44 54.324914
L 489.351818 54.324914
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.5; stroke-width: 0.8"/>
</g>
<g id="line2d_25">
<g id="line2d_27">
<g>
<use xlink:href="#mc0b9ff744f" x="57.44" y="54.324914" style="stroke: #000000; stroke-width: 0.8"/>
</g>
@ -1066,6 +1076,11 @@ z
</g>
</g>
</g>
<g id="line2d_28">
<path d="M 57.44 52.148669
L 407.383365 317.8
" clip-path="url(#pc5415b3683)" style="fill: none; stroke-dasharray: 1.6,2.64; stroke-dashoffset: 0; stroke: #b45309; stroke-width: 1.6"/>
</g>
<g id="patch_3">
<path d="M 57.44 274.84
L 57.44 25.44
@ -1406,7 +1421,7 @@ z
</g>
</g>
<g id="legend_1">
<g id="line2d_26">
<g id="line2d_29">
<path d="M 309.988068 35.91875
L 317.988068 35.91875
L 325.988068 35.91875
@ -1778,6 +1793,341 @@ z
</g>
</g>
</g>
<g id="text_29">
<!-- dieselbe Steigung, eine Stufe höher — -->
<g style="fill: #b45309" transform="translate(222.719845 134.109124) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-64" d="M 2906 2969
L 2906 4863
L 3481 4863
L 3481 0
L 2906 0
L 2906 525
Q 2725 213 2448 61
Q 2172 -91 1784 -91
Q 1150 -91 751 415
Q 353 922 353 1747
Q 353 2572 751 3078
Q 1150 3584 1784 3584
Q 2172 3584 2448 3432
Q 2725 3281 2906 2969
z
M 947 1747
Q 947 1113 1208 752
Q 1469 391 1925 391
Q 2381 391 2643 752
Q 2906 1113 2906 1747
Q 2906 2381 2643 2742
Q 2381 3103 1925 3103
Q 1469 3103 1208 2742
Q 947 2381 947 1747
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-53" d="M 3425 4513
L 3425 3897
Q 3066 4069 2747 4153
Q 2428 4238 2131 4238
Q 1616 4238 1336 4038
Q 1056 3838 1056 3469
Q 1056 3159 1242 3001
Q 1428 2844 1947 2747
L 2328 2669
Q 3034 2534 3370 2195
Q 3706 1856 3706 1288
Q 3706 609 3251 259
Q 2797 -91 1919 -91
Q 1588 -91 1214 -16
Q 841 59 441 206
L 441 856
Q 825 641 1194 531
Q 1563 422 1919 422
Q 2459 422 2753 634
Q 3047 847 3047 1241
Q 3047 1584 2836 1778
Q 2625 1972 2144 2069
L 1759 2144
Q 1053 2284 737 2584
Q 422 2884 422 3419
Q 422 4038 858 4394
Q 1294 4750 2059 4750
Q 2388 4750 2728 4690
Q 3069 4631 3425 4513
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-66" d="M 2375 4863
L 2375 4384
L 1825 4384
Q 1516 4384 1395 4259
Q 1275 4134 1275 3809
L 1275 3500
L 2222 3500
L 2222 3053
L 1275 3053
L 1275 0
L 697 0
L 697 3053
L 147 3053
L 147 3500
L 697 3500
L 697 3744
Q 697 4328 969 4595
Q 1241 4863 1831 4863
L 2375 4863
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-f6" d="M 1959 3097
Q 1497 3097 1228 2736
Q 959 2375 959 1747
Q 959 1119 1226 758
Q 1494 397 1959 397
Q 2419 397 2687 759
Q 2956 1122 2956 1747
Q 2956 2369 2687 2733
Q 2419 3097 1959 3097
z
M 1959 3584
Q 2709 3584 3137 3096
Q 3566 2609 3566 1747
Q 3566 888 3137 398
Q 2709 -91 1959 -91
Q 1206 -91 779 398
Q 353 888 353 1747
Q 353 2609 779 3096
Q 1206 3584 1959 3584
z
M 2253 4850
L 2888 4850
L 2888 4219
L 2253 4219
L 2253 4850
z
M 1031 4850
L 1666 4850
L 1666 4219
L 1031 4219
L 1031 4850
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2014" d="M 313 1978
L 6088 1978
L 6088 1528
L 313 1528
L 313 1978
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-64"/>
<use xlink:href="#DejaVuSans-69" transform="translate(63.476562 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(91.259766 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(152.783203 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(204.882812 0)"/>
<use xlink:href="#DejaVuSans-6c" transform="translate(266.40625 0)"/>
<use xlink:href="#DejaVuSans-62" transform="translate(294.189453 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(357.666016 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(419.189453 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(450.976562 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(514.453125 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(553.662109 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(615.185547 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(642.96875 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(706.445312 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(769.824219 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(833.203125 0)"/>
<use xlink:href="#DejaVuSans-2c" transform="translate(896.679688 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(928.466797 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(960.253906 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1021.777344 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1049.560547 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1112.939453 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1174.462891 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(1206.25 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1269.726562 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1308.935547 0)"/>
<use xlink:href="#DejaVuSans-66" transform="translate(1372.314453 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1407.519531 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1469.042969 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1500.830078 0)"/>
<use xlink:href="#DejaVuSans-f6" transform="translate(1564.208984 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(1625.390625 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1688.769531 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(1750.292969 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1791.40625 0)"/>
<use xlink:href="#DejaVuSans-2014" transform="translate(1823.193359 0)"/>
</g>
<!-- jede gewichtete Summe nimmt die untere -->
<g style="fill: #b45309" transform="translate(222.719845 142.507483) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-6a" d="M 603 3500
L 1178 3500
L 1178 -63
Q 1178 -731 923 -1031
Q 669 -1331 103 -1331
L -116 -1331
L -116 -844
L 38 -844
Q 366 -844 484 -692
Q 603 -541 603 -63
L 603 3500
z
M 603 4863
L 1178 4863
L 1178 4134
L 603 4134
L 603 4863
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-6a"/>
<use xlink:href="#DejaVuSans-65" transform="translate(27.783203 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(89.306641 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(152.783203 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(214.306641 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(246.09375 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(309.570312 0)"/>
<use xlink:href="#DejaVuSans-77" transform="translate(371.09375 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(452.880859 0)"/>
<use xlink:href="#DejaVuSans-63" transform="translate(480.664062 0)"/>
<use xlink:href="#DejaVuSans-68" transform="translate(535.644531 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(599.023438 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(638.232422 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(699.755859 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(738.964844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(800.488281 0)"/>
<use xlink:href="#DejaVuSans-53" transform="translate(832.275391 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(895.751953 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(959.130859 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1056.542969 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1153.955078 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1215.478516 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1247.265625 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1310.644531 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1338.427734 0)"/>
<use xlink:href="#DejaVuSans-6d" transform="translate(1435.839844 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1533.251953 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1572.460938 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(1604.248047 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(1667.724609 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1695.507812 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1757.03125 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1788.818359 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1852.197266 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1915.576172 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(1954.785156 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(2016.308594 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(2055.171875 0)"/>
</g>
</g>
<g id="text_30">
<!-- ε-Constraint: CO₂ ≤ 5.912 kg -->
<g style="fill: #059669" transform="translate(63.44 182.109124) scale(0.075 -0.075)">
<defs>
<path id="DejaVuSans-3b5" d="M 1234 1888
Q 884 1963 697 2166
Q 509 2366 509 2656
Q 509 3097 866 3344
Q 1219 3588 1831 3588
Q 2069 3588 2334 3550
Q 2600 3513 2906 3438
L 2906 2916
Q 2603 3006 2353 3047
Q 2100 3088 1878 3088
Q 1506 3088 1291 2944
Q 1072 2800 1072 2600
Q 1072 2381 1284 2241
Q 1494 2103 1856 2103
L 2341 2103
L 2341 1628
L 1878 1628
Q 1450 1628 1225 1469
Q 981 1294 981 1019
Q 981 738 1244 563
Q 1509 388 1981 388
Q 2272 388 2538 444
Q 2803 503 3025 619
L 3025 84
Q 2744 -3 2481 -47
Q 2216 -91 1966 -91
Q 1222 -91 819 194
Q 416 478 416 1019
Q 416 1356 631 1584
Q 847 1813 1234 1888
z
" transform="scale(0.015625)"/>
<path id="DejaVuSans-2264" d="M 4684 3175
L 1684 2309
L 4684 1453
L 4684 897
L 678 2047
L 678 2578
L 4684 3725
L 4684 3175
z
M 678 531
L 4684 531
L 4684 0
L 678 0
L 678 531
z
" transform="scale(0.015625)"/>
</defs>
<use xlink:href="#DejaVuSans-3b5"/>
<use xlink:href="#DejaVuSans-2d" transform="translate(54.052734 0)"/>
<use xlink:href="#DejaVuSans-43" transform="translate(90.136719 0)"/>
<use xlink:href="#DejaVuSans-6f" transform="translate(159.960938 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(221.142578 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(284.521484 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(336.621094 0)"/>
<use xlink:href="#DejaVuSans-72" transform="translate(375.830078 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(416.943359 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(478.222656 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(506.005859 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(569.384766 0)"/>
<use xlink:href="#DejaVuSans-3a" transform="translate(608.59375 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(642.285156 0)"/>
<use xlink:href="#DejaVuSans-43" transform="translate(674.072266 0)"/>
<use xlink:href="#DejaVuSans-4f" transform="translate(743.896484 0)"/>
<use xlink:href="#DejaVuSans-2082" transform="translate(822.607422 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(862.695312 0)"/>
<use xlink:href="#DejaVuSans-2264" transform="translate(894.482422 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(978.271484 0)"/>
<use xlink:href="#DejaVuSans-35" transform="translate(1010.058594 0)"/>
<use xlink:href="#DejaVuSans-2e" transform="translate(1073.681641 0)"/>
<use xlink:href="#DejaVuSans-39" transform="translate(1105.46875 0)"/>
<use xlink:href="#DejaVuSans-31" transform="translate(1169.091797 0)"/>
<use xlink:href="#DejaVuSans-32" transform="translate(1232.714844 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(1296.337891 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(1328.125 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(1386.035156 0)"/>
</g>
<!-- findet genau diesen Punkt -->
<g style="fill: #059669" transform="translate(63.44 190.507483) scale(0.075 -0.075)">
<use xlink:href="#DejaVuSans-66"/>
<use xlink:href="#DejaVuSans-69" transform="translate(35.205078 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(62.988281 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(126.367188 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(189.84375 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(251.367188 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(290.576172 0)"/>
<use xlink:href="#DejaVuSans-67" transform="translate(322.363281 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(385.839844 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(447.363281 0)"/>
<use xlink:href="#DejaVuSans-61" transform="translate(510.742188 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(572.021484 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(635.400391 0)"/>
<use xlink:href="#DejaVuSans-64" transform="translate(667.1875 0)"/>
<use xlink:href="#DejaVuSans-69" transform="translate(730.664062 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(758.447266 0)"/>
<use xlink:href="#DejaVuSans-73" transform="translate(819.970703 0)"/>
<use xlink:href="#DejaVuSans-65" transform="translate(872.070312 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(933.59375 0)"/>
<use xlink:href="#DejaVuSans-20" transform="translate(996.972656 0)"/>
<use xlink:href="#DejaVuSans-50" transform="translate(1028.759766 0)"/>
<use xlink:href="#DejaVuSans-75" transform="translate(1087.3125 0)"/>
<use xlink:href="#DejaVuSans-6e" transform="translate(1150.691406 0)"/>
<use xlink:href="#DejaVuSans-6b" transform="translate(1214.070312 0)"/>
<use xlink:href="#DejaVuSans-74" transform="translate(1271.980469 0)"/>
</g>
</g>
</g>
</g>
<defs>

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 160 KiB