Phase 6.3: Parallelitaet und Determinismus - gemessen statt behauptet

Neuer Abschnitt im CP-SAT-Kapitel plus Parallele_Suche.py (76. Programm).
Setzt Paket 2 aus Verbesserungen_02.md um.

Was fehlte, war nicht die Regel, sondern die Messung: Das Buch setzt an acht
Stellen num_workers = 1 mit dem Kommentar "fuer eine reproduzierbare
Ausgabe", nachgeprueft hatte es nie jemand. Gerechnet wird auf demselben
Job-Shop wie das Intervallvariablen-Kapitel, nur gross genug, dass die Suche
arbeitet: 12 Auftraege, 10 Maschinen, 120 Arbeitsgaenge.

Zwei Befunde, beide staerker als die Behauptung:

* Die Beschleunigung ist ueberlinear. Acht Arbeiter waren im abgedruckten
  Lauf nicht achtmal, sondern 12,3-mal schneller als einer. Kein Messfehler:
  CP-SAT vervielfacht nicht dieselbe Suche, sondern laesst verschiedene
  Strategien nebeneinander laufen, die einander ihre Schranken mitteilen.
* Der Seed genuegt nicht - und zwar schon ab ZWEI Arbeitern. Ein Arbeiter:
  1 Plan aus 4 Laeufen. Zwei Arbeiter: 3 verschiedene Plaene aus 4 Laeufen,
  bei identischem random_seed und identischem Zielwert 183.

Die vollstaendige Antwort kam erst ueber die Uebungsaufgabe: Mit
num_workers = 1, aber fuenf verschiedenen Seeds ergeben sich ebenfalls fuenf
verschiedene Plaene. Keiner der beiden Parameter sichert die
Reproduzierbarkeit allein - erst die Kombination traegt.

Weiter fuer die Aufgabe gemessen: Der Gewinn kehrt sich um (auf 24 Kernen
Faktor 10,2 bei 8 Arbeitern, 12,3 bei 16, 9,0 bei 24) - "so viele Arbeiter
wie Kerne" ist damit widerlegt. Und bei 15 Auftraegen laeuft ein Arbeiter
ins 60-s-Limit (FEASIBLE, Makespan 200), waehrend acht OPTIMAL mit
demselben Makespan 200 nach 26,4 s melden: Der Unterschied liegt nicht in
der Loesung, sondern im Beweis, dass es keine bessere gibt.

Die abgedruckte Ausgabe traegt die Kennzeichnung "Laufzeiten und die Zahl
der verschiedenen Plaene sind hardwareabhaengig" - nach dem Muster, das das
Testing-Kapitel fuer Benchmark_Skalierung.py schon verwendet. Der Vergleich
des extrahierten Programms mit dem Abdruck weicht denn auch in genau einer
Zelle ab (4 statt 3 verschiedene Plaene bei 4 Arbeitern); Zielwert und
Struktur sind identisch. Hier ist die Nichtreproduzierbarkeit der
abgedruckten Zahl die Aussage selbst.

Mitgezogen: Kapitelkopf, Lernziele, Selbsttest, Zusammenfassung,
Vorwort-Programmverzeichnis, Uebungsaufgabe und Loesung in Anhang A, ein
Verweis aus dem bestehenden Callout zu mehrdeutigen Optima und einer aus
Warmstart_Effekt.py im MILP-Kapitel (dort nach Regel 12 der Kapitelname).

Stand: 295 Abschnitte, 730 Querverweise, 328 Indexmarken, 76 Programme,
140 Aufgaben mit 140 Loesungen, 33 pytest-Tests, PDF 744 Seiten, 69
netzfreie Programme fehlerfrei.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dschlueter 2026-09-08 11:19:53 +02:00
commit 293370b7a1
30 changed files with 12643 additions and 10385 deletions

View file

@ -1342,68 +1342,69 @@ mit Warm-Start 8.44s 150 69,134.14</code></pre>
<span id="cb12-83"><a href="#cb12-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-84"><a href="#cb12-84" aria-hidden="true" tabindex="-1"></a> loeser <span class="op">=</span> cp_model.CpSolver()</span>
<span id="cb12-85"><a href="#cb12-85" aria-hidden="true" tabindex="-1"></a> loeser.parameters.max_time_in_seconds <span class="op">=</span> zeitlimit</span>
<span id="cb12-86"><a href="#cb12-86" aria-hidden="true" tabindex="-1"></a> <span class="co"># Ein Arbeiter und fester Startwert, damit die Messung reproduzierbar ist.</span></span>
<span id="cb12-87"><a href="#cb12-87" aria-hidden="true" tabindex="-1"></a> <span class="co"># Im Produktivbetrieb laesst man beides auf den Standardwerten.</span></span>
<span id="cb12-88"><a href="#cb12-88" aria-hidden="true" tabindex="-1"></a> loeser.parameters.num_workers <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb12-89"><a href="#cb12-89" aria-hidden="true" tabindex="-1"></a> loeser.parameters.random_seed <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb12-90"><a href="#cb12-90" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-91"><a href="#cb12-91" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
<span id="cb12-92"><a href="#cb12-92" aria-hidden="true" tabindex="-1"></a> status <span class="op">=</span> loeser.Solve(modell)</span>
<span id="cb12-93"><a href="#cb12-93" aria-hidden="true" tabindex="-1"></a> dauer_s <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
<span id="cb12-94"><a href="#cb12-94" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-95"><a href="#cb12-95" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> status <span class="kw">not</span> <span class="kw">in</span> (cp_model.OPTIMAL, cp_model.FEASIBLE):</span>
<span id="cb12-96"><a href="#cb12-96" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">RuntimeError</span>(<span class="ss">f&quot;Kein Plan gefunden: </span><span class="sc">{</span>loeser<span class="sc">.</span>StatusName(status)<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-97"><a href="#cb12-97" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> loeser.StatusName(status), <span class="bu">int</span>(loeser.ObjectiveValue()), dauer_s</span>
<span id="cb12-98"><a href="#cb12-98" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-86"><a href="#cb12-86" aria-hidden="true" tabindex="-1"></a> <span class="co"># Ein Arbeiter und fester Startwert, damit die Messung reproduzierbar ist -</span></span>
<span id="cb12-87"><a href="#cb12-87" aria-hidden="true" tabindex="-1"></a> <span class="co"># der Seed allein genuegt dafuer NICHT (Kapitel Constraint Programming).</span></span>
<span id="cb12-88"><a href="#cb12-88" aria-hidden="true" tabindex="-1"></a> <span class="co"># Im Produktivbetrieb laesst man beides auf den Standardwerten.</span></span>
<span id="cb12-89"><a href="#cb12-89" aria-hidden="true" tabindex="-1"></a> loeser.parameters.num_workers <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb12-90"><a href="#cb12-90" aria-hidden="true" tabindex="-1"></a> loeser.parameters.random_seed <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb12-91"><a href="#cb12-91" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-92"><a href="#cb12-92" aria-hidden="true" tabindex="-1"></a> t0 <span class="op">=</span> time.perf_counter()</span>
<span id="cb12-93"><a href="#cb12-93" aria-hidden="true" tabindex="-1"></a> status <span class="op">=</span> loeser.Solve(modell)</span>
<span id="cb12-94"><a href="#cb12-94" aria-hidden="true" tabindex="-1"></a> dauer_s <span class="op">=</span> time.perf_counter() <span class="op">-</span> t0</span>
<span id="cb12-95"><a href="#cb12-95" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-96"><a href="#cb12-96" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> status <span class="kw">not</span> <span class="kw">in</span> (cp_model.OPTIMAL, cp_model.FEASIBLE):</span>
<span id="cb12-97"><a href="#cb12-97" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">RuntimeError</span>(<span class="ss">f&quot;Kein Plan gefunden: </span><span class="sc">{</span>loeser<span class="sc">.</span>StatusName(status)<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-98"><a href="#cb12-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> loeser.StatusName(status), <span class="bu">int</span>(loeser.ObjectiveValue()), dauer_s</span>
<span id="cb12-99"><a href="#cb12-99" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-100"><a href="#cb12-100" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb12-101"><a href="#cb12-101" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-102"><a href="#cb12-102" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WARM-START: WENN DIE HEURISTIK MEHR WEISS ALS DER SOLVER&quot;</span>)</span>
<span id="cb12-103"><a href="#cb12-103" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-104"><a href="#cb12-104" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Lastverteilung: Auftraege auf gleichartige Maschinen verteilen,&quot;</span>)</span>
<span id="cb12-105"><a href="#cb12-105" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;sodass die letzte Maschine so frueh wie moeglich fertig wird.</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb12-106"><a href="#cb12-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-107"><a href="#cb12-107" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Instanz&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Variante&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Makespan&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Zeit&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-108"><a href="#cb12-108" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Faktor&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-109"><a href="#cb12-109" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-110"><a href="#cb12-110" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-111"><a href="#cb12-111" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> n_auftraege, n_maschinen <span class="kw">in</span> [(<span class="dv">60</span>, <span class="dv">7</span>), (<span class="dv">80</span>, <span class="dv">9</span>)]:</span>
<span id="cb12-112"><a href="#cb12-112" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> RNG.integers(<span class="dv">10</span>, <span class="dv">90</span>, n_auftraege)</span>
<span id="cb12-113"><a href="#cb12-113" aria-hidden="true" tabindex="-1"></a> start, lpt_wert <span class="op">=</span> lpt_heuristik(dauer, n_maschinen)</span>
<span id="cb12-114"><a href="#cb12-114" aria-hidden="true" tabindex="-1"></a> untere_schranke <span class="op">=</span> dauer.<span class="bu">sum</span>() <span class="op">/</span> n_maschinen</span>
<span id="cb12-115"><a href="#cb12-115" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-116"><a href="#cb12-116" aria-hidden="true" tabindex="-1"></a> instanz <span class="op">=</span> <span class="ss">f&quot;</span><span class="sc">{</span>n_auftraege<span class="sc">}</span><span class="ss"> Auftr., </span><span class="sc">{</span>n_maschinen<span class="sc">}</span><span class="ss"> Masch.&quot;</span></span>
<span id="cb12-117"><a href="#cb12-117" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>instanz<span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;LPT-Heuristik&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>lpt_wert<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-118"><a href="#cb12-118" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&lt; 0.001s&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-119"><a href="#cb12-119" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-120"><a href="#cb12-120" aria-hidden="true" tabindex="-1"></a> _, ziel_kalt, zeit_kalt <span class="op">=</span> loese(dauer, n_maschinen, <span class="fl">60.0</span>)</span>
<span id="cb12-121"><a href="#cb12-121" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;CP-SAT kalt&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>ziel_kalt<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-122"><a href="#cb12-122" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>zeit_kalt<span class="sc">:&gt;8.3f}</span><span class="ss">s </span><span class="sc">{</span><span class="st">&#39;1,0x&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-123"><a href="#cb12-123" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-124"><a href="#cb12-124" aria-hidden="true" tabindex="-1"></a> _, ziel_warm, zeit_warm <span class="op">=</span> loese(dauer, n_maschinen, <span class="fl">60.0</span>, hinweis<span class="op">=</span>start)</span>
<span id="cb12-125"><a href="#cb12-125" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;CP-SAT + LPT-Hinweis&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>ziel_warm<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-126"><a href="#cb12-126" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>zeit_warm<span class="sc">:&gt;8.3f}</span><span class="ss">s </span><span class="sc">{</span>zeit_kalt <span class="op">/</span> zeit_warm<span class="sc">:&gt;7.1f}</span><span class="ss">x&quot;</span>)</span>
<span id="cb12-127"><a href="#cb12-127" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-128"><a href="#cb12-128" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> ziel_kalt <span class="op">==</span> ziel_warm, <span class="op">\</span></span>
<span id="cb12-129"><a href="#cb12-129" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Der Hinweis darf das Optimum nicht veraendern - nur den Weg dorthin!&quot;</span></span>
<span id="cb12-130"><a href="#cb12-130" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;untere Schranke&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>untere_schranke<span class="sc">:&gt;9.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-131"><a href="#cb12-131" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-132"><a href="#cb12-132" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-133"><a href="#cb12-133" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Drei Beobachtungen:&quot;</span>)</span>
<span id="cb12-134"><a href="#cb12-134" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;1. Der Hinweis aendert das ERGEBNIS nicht - beide Laeufe finden&quot;</span>)</span>
<span id="cb12-135"><a href="#cb12-135" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; dasselbe Optimum. Er aendert nur, wie lange der Beweis dauert.&quot;</span>)</span>
<span id="cb12-136"><a href="#cb12-136" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Genau deshalb ist ein Warm-Start ungefaehrlich: Ein schlechter&quot;</span>)</span>
<span id="cb12-137"><a href="#cb12-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Hinweis kostet Zeit, er verfaelscht aber nie die Loesung.&quot;</span>)</span>
<span id="cb12-138"><a href="#cb12-138" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;2. Die LPT-Heuristik liegt schon sehr nah am Optimum. Ihr Wert fuer&quot;</span>)</span>
<span id="cb12-139"><a href="#cb12-139" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; den Solver liegt weniger in der Qualitaet als darin, dass sie&quot;</span>)</span>
<span id="cb12-140"><a href="#cb12-140" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; SOFORT da ist - der Solver kann von Beginn an alles verwerfen,&quot;</span>)</span>
<span id="cb12-141"><a href="#cb12-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; was schlechter ist.&quot;</span>)</span>
<span id="cb12-142"><a href="#cb12-142" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;3. Der Faktor schwankt von Instanz zu Instanz - oben 2,2x und 1,4x -&quot;</span>)</span>
<span id="cb12-143"><a href="#cb12-143" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; und laesst sich NICHT aus der Problemgroesse ableiten. Er haengt&quot;</span>)</span>
<span id="cb12-144"><a href="#cb12-144" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; davon ab, wie schnell der Solver von allein eine vergleichbar gute&quot;</span>)</span>
<span id="cb12-145"><a href="#cb12-145" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Loesung findet. Das ist die eigentliche Lehre: Ein Warm-Start ist&quot;</span>)</span>
<span id="cb12-146"><a href="#cb12-146" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; eine Messung wert, keine Glaubensfrage.&quot;</span>)</span>
<span id="cb12-147"><a href="#cb12-147" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span></code></pre></div>
<span id="cb12-100"><a href="#cb12-100" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-101"><a href="#cb12-101" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb12-102"><a href="#cb12-102" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-103"><a href="#cb12-103" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; WARM-START: WENN DIE HEURISTIK MEHR WEISS ALS DER SOLVER&quot;</span>)</span>
<span id="cb12-104"><a href="#cb12-104" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-105"><a href="#cb12-105" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;Lastverteilung: Auftraege auf gleichartige Maschinen verteilen,&quot;</span>)</span>
<span id="cb12-106"><a href="#cb12-106" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;sodass die letzte Maschine so frueh wie moeglich fertig wird.</span><span class="ch">\n</span><span class="st">&quot;</span>)</span>
<span id="cb12-107"><a href="#cb12-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-108"><a href="#cb12-108" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Instanz&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Variante&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Makespan&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Zeit&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-109"><a href="#cb12-109" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Faktor&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-110"><a href="#cb12-110" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-111"><a href="#cb12-111" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-112"><a href="#cb12-112" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> n_auftraege, n_maschinen <span class="kw">in</span> [(<span class="dv">60</span>, <span class="dv">7</span>), (<span class="dv">80</span>, <span class="dv">9</span>)]:</span>
<span id="cb12-113"><a href="#cb12-113" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> RNG.integers(<span class="dv">10</span>, <span class="dv">90</span>, n_auftraege)</span>
<span id="cb12-114"><a href="#cb12-114" aria-hidden="true" tabindex="-1"></a> start, lpt_wert <span class="op">=</span> lpt_heuristik(dauer, n_maschinen)</span>
<span id="cb12-115"><a href="#cb12-115" aria-hidden="true" tabindex="-1"></a> untere_schranke <span class="op">=</span> dauer.<span class="bu">sum</span>() <span class="op">/</span> n_maschinen</span>
<span id="cb12-116"><a href="#cb12-116" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-117"><a href="#cb12-117" aria-hidden="true" tabindex="-1"></a> instanz <span class="op">=</span> <span class="ss">f&quot;</span><span class="sc">{</span>n_auftraege<span class="sc">}</span><span class="ss"> Auftr., </span><span class="sc">{</span>n_maschinen<span class="sc">}</span><span class="ss"> Masch.&quot;</span></span>
<span id="cb12-118"><a href="#cb12-118" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>instanz<span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;LPT-Heuristik&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>lpt_wert<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-119"><a href="#cb12-119" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&lt; 0.001s&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-120"><a href="#cb12-120" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-121"><a href="#cb12-121" aria-hidden="true" tabindex="-1"></a> _, ziel_kalt, zeit_kalt <span class="op">=</span> loese(dauer, n_maschinen, <span class="fl">60.0</span>)</span>
<span id="cb12-122"><a href="#cb12-122" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;CP-SAT kalt&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>ziel_kalt<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-123"><a href="#cb12-123" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>zeit_kalt<span class="sc">:&gt;8.3f}</span><span class="ss">s </span><span class="sc">{</span><span class="st">&#39;1,0x&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-124"><a href="#cb12-124" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-125"><a href="#cb12-125" aria-hidden="true" tabindex="-1"></a> _, ziel_warm, zeit_warm <span class="op">=</span> loese(dauer, n_maschinen, <span class="fl">60.0</span>, hinweis<span class="op">=</span>start)</span>
<span id="cb12-126"><a href="#cb12-126" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;CP-SAT + LPT-Hinweis&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>ziel_warm<span class="sc">:&gt;9}</span><span class="ss"> &quot;</span></span>
<span id="cb12-127"><a href="#cb12-127" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>zeit_warm<span class="sc">:&gt;8.3f}</span><span class="ss">s </span><span class="sc">{</span>zeit_kalt <span class="op">/</span> zeit_warm<span class="sc">:&gt;7.1f}</span><span class="ss">x&quot;</span>)</span>
<span id="cb12-128"><a href="#cb12-128" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-129"><a href="#cb12-129" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> ziel_kalt <span class="op">==</span> ziel_warm, <span class="op">\</span></span>
<span id="cb12-130"><a href="#cb12-130" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Der Hinweis darf das Optimum nicht veraendern - nur den Weg dorthin!&quot;</span></span>
<span id="cb12-131"><a href="#cb12-131" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;untere Schranke&#39;</span><span class="sc">:&lt;22}</span><span class="ss"> </span><span class="sc">{</span>untere_schranke<span class="sc">:&gt;9.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb12-132"><a href="#cb12-132" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span>
<span id="cb12-133"><a href="#cb12-133" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-134"><a href="#cb12-134" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">Drei Beobachtungen:&quot;</span>)</span>
<span id="cb12-135"><a href="#cb12-135" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;1. Der Hinweis aendert das ERGEBNIS nicht - beide Laeufe finden&quot;</span>)</span>
<span id="cb12-136"><a href="#cb12-136" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; dasselbe Optimum. Er aendert nur, wie lange der Beweis dauert.&quot;</span>)</span>
<span id="cb12-137"><a href="#cb12-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Genau deshalb ist ein Warm-Start ungefaehrlich: Ein schlechter&quot;</span>)</span>
<span id="cb12-138"><a href="#cb12-138" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Hinweis kostet Zeit, er verfaelscht aber nie die Loesung.&quot;</span>)</span>
<span id="cb12-139"><a href="#cb12-139" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;2. Die LPT-Heuristik liegt schon sehr nah am Optimum. Ihr Wert fuer&quot;</span>)</span>
<span id="cb12-140"><a href="#cb12-140" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; den Solver liegt weniger in der Qualitaet als darin, dass sie&quot;</span>)</span>
<span id="cb12-141"><a href="#cb12-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; SOFORT da ist - der Solver kann von Beginn an alles verwerfen,&quot;</span>)</span>
<span id="cb12-142"><a href="#cb12-142" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; was schlechter ist.&quot;</span>)</span>
<span id="cb12-143"><a href="#cb12-143" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;3. Der Faktor schwankt von Instanz zu Instanz - oben 2,2x und 1,4x -&quot;</span>)</span>
<span id="cb12-144"><a href="#cb12-144" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; und laesst sich NICHT aus der Problemgroesse ableiten. Er haengt&quot;</span>)</span>
<span id="cb12-145"><a href="#cb12-145" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; davon ab, wie schnell der Solver von allein eine vergleichbar gute&quot;</span>)</span>
<span id="cb12-146"><a href="#cb12-146" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; Loesung findet. Das ist die eigentliche Lehre: Ein Warm-Start ist&quot;</span>)</span>
<span id="cb12-147"><a href="#cb12-147" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; eine Messung wert, keine Glaubensfrage.&quot;</span>)</span>
<span id="cb12-148"><a href="#cb12-148" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">78</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe (Zeiten hardwareabhängig):</strong></p>
<pre><code>==============================================================================
WARM-START: WENN DIE HEURISTIK MEHR WEISS ALS DER SOLVER