<aclass="brand"href="index.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-book"></use></svg><span>Optimierte Entscheidungsfindung mit Python</span></a>
<navclass="sidebar"id="sidebar"aria-label="Kapitelnavigation"><divclass="sidebar-inhalt"><detailsclass="sidebar-gruppe"><summary>Einstieg</summary><ul><lidata-kapitel="vorwort.html"><ahref="vorwort.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><lidata-kapitel="notation.html"><ahref="notation.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><lidata-kapitel="einfuehrung.html"><ahref="einfuehrung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><lidata-kapitel="fundament.html"><ahref="fundament.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><lidata-kapitel="oekosystem.html"><ahref="oekosystem.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><lidata-kapitel="modellierung.html"><ahref="modellierung.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><lidata-kapitel="synthese-grundlagen.html"><ahref="synthese-grundlagen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><detailsclass="sidebar-gruppe"open><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><lidata-kapitel="lp.html"><ahref="lp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><lidata-kapitel="milp.html"><ahref="milp.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><lidata-kapitel="cpsat.html"><ahref="cpsat.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><lidata-kapitel="graphen.html"><ahref="graphen.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><lidata-kapitel="metaheuristiken.html"><ahref="metaheuristiken.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><lidata-kapitel="dekomposition.html"class="aktiv"><ahref="dekomposition.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><lidata-kapitel="synthese-kernverfahren.html"><ahref="synthese-kernverfahren.html"><spanclass="fortschritt-haken"><svgclass="icon"aria-hidden="true"><usehref="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><detailsclass="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit
<navclass="breadcrumb"aria-label="Breadcrumb"><ahref="index.html">Start</a>›<span>Teil II</span>›<span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></nav>
<navclass="prev-next"><aclass="prev-next-knopf prev-next-prev"href="metaheuristiken.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a><aclass="prev-next-knopf prev-next-next"href="synthese-kernverfahren.html"><span><small>Weiter</small>Synthese Teil II</span><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-right"></use></svg></a></nav>
<h1id="kap-dekomposition">Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</h1>
<divclass="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Um die andere Antwort auf die Frage aus <ahref="metaheuristiken.html#kap-metaheuristiken">Kapitel 9</a>: Was tun, wenn der Solver an der Größe scheitert? Statt die Lösung zu approximieren, formuliert man das Modell so um, dass exaktes Lösen wieder möglich wird.</p>
<p><strong>Voraussetzungen:</strong><ahref="lp.html#sec:lp-dualitaet-und-schattenpreise">Abschnitt 5.6</a> (Dualwerte) und <ahref="milp.html#kap-milp">Kapitel 6</a> (<code>Rucksack.py</code>). Beides wird gebraucht, aber nicht neu erklärt.</p>
<p><strong>Danach können Sie:</strong> ein Problem in Master und Teilproblem zerlegen; die Spaltengenerierungsschleife selbst schreiben; und begründen, wann sich der Aufwand lohnt — die Antwort hängt an einer einzigen Kennzahl der Instanz.</p>
<p><strong>Zeitbedarf:</strong> ca. 3 Stunden.</p>
<p><strong>Notebook:</strong><ahref="Notebooks_04/dekomposition.ipynb">dekomposition.ipynb</a> — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten</p>
<h2id="sec:dekomposition-schnellstart">10.1 In 5 Minuten gelöst</h2>
<divclass="card card-schnellstart">
<blockquote>
<p><strong>🚀 In 5 Minuten gelöst: Denken Sie in Mustern, nicht in Stücken</strong></p>
<p>Eine Rolle ist 1 000 mm breit. Bestellt sind 9 Stück à 420 mm, 12 à 310 mm und 14 à 250 mm. Wie viele Rollen braucht man?</p>
<p>Die naheliegende Frage — <em>„welches Stück kommt auf welche Rolle?“</em> — führt zu 35 Einzelentscheidungen. Die bessere Frage lautet: <em>„Wie oft schneide ich welches Muster?“</em></p>
<spanid="cb1-5"><ahref="#cb1-5"aria-hidden="true"tabindex="-1"></a>ROLLE <spanclass="op">=</span><spanclass="dv">1000</span><spanclass="co"># mm Mutterrolle</span></span>
<spanid="cb1-8"><ahref="#cb1-8"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Alle Schnittmuster aufzaehlen, die in die Rolle passen</span></span>
<spanid="cb1-9"><ahref="#cb1-9"aria-hidden="true"tabindex="-1"></a>muster <spanclass="op">=</span> [m <spanclass="cf">for</span> m <spanclass="kw">in</span> itertools.product(<spanclass="op">*</span>[<spanclass="bu">range</span>(ROLLE <spanclass="op">//</span> b <spanclass="op">+</span><spanclass="dv">1</span>) <spanclass="cf">for</span> b <spanclass="kw">in</span> breiten])</span>
<spanid="cb1-10"><ahref="#cb1-10"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span><spanclass="bu">sum</span>(a <spanclass="op">*</span> b <spanclass="cf">for</span> a, b <spanclass="kw">in</span><spanclass="bu">zip</span>(m, breiten)) <spanclass="op"><=</span> ROLLE <spanclass="kw">and</span><spanclass="bu">sum</span>(m) <spanclass="op">></span><spanclass="dv">0</span>]</span>
<spanid="cb1-12"><ahref="#cb1-12"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Wie oft schneide ich welches Muster? min sum(x) u.d.N. A x >= bedarf</span></span>
<spanid="cb1-18"><ahref="#cb1-18"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> anzahl, m <spanclass="kw">in</span><spanclass="bu">zip</span>(np.<spanclass="bu">round</span>(loesung.x).astype(<spanclass="bu">int</span>), muster):</span>
<spanid="cb1-20"><ahref="#cb1-20"aria-hidden="true"tabindex="-1"></a> rest <spanclass="op">=</span> ROLLE <spanclass="op">-</span><spanclass="bu">sum</span>(a <spanclass="op">*</span> b <spanclass="cf">for</span> a, b <spanclass="kw">in</span><spanclass="bu">zip</span>(m, breiten))</span>
<spanid="cb1-21"><ahref="#cb1-21"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>anzahl<spanclass="sc">:2d}</span><spanclass="ss"> x "</span><spanclass="op">+</span><spanclass="st">""</span>.join(<spanclass="ss">f"</span><spanclass="sc">{</span>a<spanclass="sc">}</span><spanclass="ss">x</span><spanclass="sc">{</span>b<spanclass="sc">}</span><spanclass="ss">mm"</span><spanclass="cf">for</span> a, b <spanclass="kw">in</span><spanclass="bu">zip</span>(m, breiten))</span>
9 x 1x420mm 1x310mm 1x250mm Verschnitt 20 mm</code></pre>
</blockquote>
</div>
<p><strong>Und jetzt der Punkt.</strong> Sehen Sie sich an, was da herauskommt: <strong>drei Schnittmuster</strong>. Kein Zuordnungsplan für 35 Stücke, sondern eine Anweisung, die an der Maschine hängen kann — neunmal dieses Muster, zweimal jenes, einmal das dritte.</p>
<p>Das ist kein kosmetischer Unterschied. Das Modell hat <strong>eine Variable je Muster</strong> statt einer je Stück-und-Rolle, und es kennt gar keine einzelnen Rollen mehr. Damit verschwindet ein Problem, das das naheliegende Modell praktisch unlösbar macht — dazu gleich mehr.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Die schwierigste Arbeit an einem Optimierungsmodell ist selten die Zielfunktion. Es ist die Frage, <strong>worüber</strong> die Variablen laufen.</p>
</blockquote>
<p><strong>Warum funktioniert das?</strong> Weil hier alle 16 möglichen Muster aufzählbar waren. Bei drei Breiten und einer 1 000-mm-Rolle sind es 16; bei dreizehn Breiten und 5 600 mm sind es zehntausende, und bei einer echten Papierfabrik mehr, als sich speichern lässt. Das ganze Kapitel handelt davon, wie man mit Mustern rechnet, ohne sie aufzuschreiben.</p>
<li>… das Abbruchkriterium über die <strong>reduzierten Kosten</strong> begründen.</li>
<li>… an einer Kennzahl der Instanz abschätzen, ob sich das Verfahren lohnt.</li>
</ol>
<hr/>
<h2id="sec:dekomposition-symmetrie">10.3 Warum das naheliegende Modell scheitert</h2>
<p>Der erste Entwurf eines Zuschnittmodells sieht fast immer so aus: eine Binärvariable <spanclass="math inline">z_{jr}</span> für „Stück <spanclass="math inline">j</span> liegt auf Rolle <spanclass="math inline">r</span>“, dazu <spanclass="math inline">y_r</span> für „Rolle <spanclass="math inline">r</span> wird benutzt”.</p>
<p>Zwei Dinge gehen dabei schief.</p>
<p><strong>Die Größe.</strong> Bei 219 Zuschnitten und höchstens 82 Rollen sind das knapp 18 000 Binärvariablen — unangenehm, aber allein noch kein Hindernis.</p>
<p><strong>Die Symmetrie.</strong> Das ist das eigentliche Problem. Alle Mutterrollen sind <strong>gleich</strong>. Jede Lösung existiert deshalb in unzähligen Umbenennungen: Vertauscht man Rolle 3 und Rolle 47, entsteht eine formal andere, inhaltlich identische Lösung. Branch-and-Bound weiß das nicht und arbeitet sie einzeln ab.</p>
<blockquote>
<p><strong>⚠️ Woran man ein Symmetrieproblem erkennt</strong></p>
<p>Der Suchbaum wächst, aber die <strong>Schranke bewegt sich nicht.</strong> Im Protokoll aus <ahref="milp.html#sec:milp-gap">Abschnitt 6.8</a> sieht das so aus: Die Zahl der Knoten steigt in die Hunderttausende, der Incumbent verbessert sich hin und wieder, und der Dual Bound steht praktisch still.</p>
<p>Das ist ein anderes Bild als „das Problem ist einfach zu groß”. Bei einem großen, aber unsymmetrischen Problem nähern sich beide Werte einander an, nur langsam.</p>
</blockquote>
<p>Das Mustermodell hat dieses Problem nicht, weil es <strong>keine einzelnen Rollen kennt</strong>. Es zählt nur, wie oft welches Schnittmuster geschnitten wird:</p>
<p><spanclass="math display">
\min \sum_{p \in P} x_p
\qquad \text{unter} \qquad
\sum_{p \in P} a_{ip}\, x_p \ge d_i \quad \forall i,
\qquad x_p \ge 0 \ \text{ganzzahlig}
</span></p>
<divclass="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer</strong></p>
<table>
<colgroup>
<colstyle="width: 50%"/>
<colstyle="width: 50%"/>
</colgroup>
<thead>
<trclass="header">
<th>Mathematik</th>
<th>Alltagssprache</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td><spanclass="math inline">P</span></td>
<td>die Menge aller zulässigen Schnittmuster</td>
</tr>
<trclass="even">
<td><spanclass="math inline">a_{ip}</span></td>
<td>„Wie viele Stücke der Breite <spanclass="math inline">i</span> liefert Muster <spanclass="math inline">p</span>?”</td>
</tr>
<trclass="odd">
<td><spanclass="math inline">x_p</span></td>
<td>„Wie oft schneide ich Muster <spanclass="math inline">p</span>?” — die einzige Entscheidung</td>
</tr>
<trclass="even">
<td><spanclass="math inline">d_i</span></td>
<td>die bestellte Stückzahl der Breite <spanclass="math inline">i</span></td>
<p><strong>Ohne Formel gesagt:</strong> „Stelle eine Schnittliste zusammen, die alle Bestellungen deckt, und benutze dabei so wenige Rollen wie möglich.”</p>
<p>Das Modell ist verblüffend klein — eine Zeile je Breite. Sein ganzes Gewicht steckt in <spanclass="math inline">P</span>, und das ist der Haken.</p>
</blockquote>
</div>
<hr/>
<h2id="sec:dekomposition-schleife">10.4 Rechnen mit Mustern, ohne sie aufzuschreiben</h2>
<p>Die Idee ist alt (Gilmore und Gomory, 1961) und in einem Satz gesagt:</p>
<blockquote>
<p>Fange mit wenigen Mustern an. Frage nach jeder Lösung, ob es ein <strong>noch nicht bekanntes</strong> Muster gibt, das sich lohnen würde — und wenn ja, nimm nur dieses eine dazu.</p>
</blockquote>
<p>Die Frage „lohnt sich noch ein Muster?” beantworten die <strong>Dualwerte</strong> des Master-LPs. Der Dualwert <spanclass="math inline">\pi_i</span> zur Bedarfszeile <spanclass="math inline">i</span> ist genau der Schattenpreis aus <ahref="lp.html#sec:lp-dualitaet-und-schattenpreise">Abschnitt 5.6</a>: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite <spanclass="math inline">i</span> kostet.</p>
<imgsrc="bilder_04/kap_dekomposition_regelkreis.svg"alt="Abb. 10.1: Der Kreislauf zwischen Master-LP und Pricing-Problem. Erzeugt von bilder_04/erzeuge_spaltengenerierung_flow.py."/>
<figcaptionaria-hidden="true">Abb. 10.1: Der Kreislauf zwischen Master-LP und Pricing-Problem. Erzeugt von <code>bilder_04/erzeuge_spaltengenerierung_flow.py</code>.</figcaption>
</figure>
<p><strong>Was Sie in der Abbildung sehen.</strong> Die Information fließt in beide Richtungen: Preise nach rechts, Muster nach links. Der Ausgang unten wird erst geöffnet, wenn das Pricing nichts mehr findet — und genau das ist der Optimalitätsbeweis.</p>
<td>„So viel ist das, was dabei herauskommt, zu den aktuellen Schattenpreisen wert.”</td>
</tr>
<trclass="odd">
<td><spanclass="math inline">> 1</span></td>
<td>„Es bringt mehr, als es kostet — dieses Muster nehmen wir dazu.”</td>
</tr>
<trclass="even">
<td><spanclass="math inline">\le 1</span></td>
<td>„Kein Muster lohnt sich mehr. <strong>Fertig</strong> — und zwar beweisbar.”</td>
</tr>
</tbody>
</table>
<p>Die letzte Zeile ist der Grund, warum das Verfahren <strong>exakt</strong> ist und nicht heuristisch: Wenn kein Muster mehr lohnende reduzierte Kosten hat, ist die LP-Lösung über <em>allen</em> Mustern optimal — auch über den nie erzeugten.</p>
</blockquote>
</div>
<p><strong>Und wie findet man das beste neue Muster?</strong> Man sucht die Zusammenstellung von Stücken mit dem größten Gesamtwert, die noch in die Rolle passt. Das ist ein <strong>Rucksackproblem</strong>:</p>
<spanid="cb3-5"><ahref="#cb3-5"aria-hidden="true"tabindex="-1"></a><spanclass="co">Kapitel Dekomposition: Das Modell umbauen, statt die Loesung zu raten.</span></span>
<spanid="cb3-7"><ahref="#cb3-7"aria-hidden="true"tabindex="-1"></a><spanclass="co">Eine Papierfabrik liefert Rollen von 5.600 mm Breite. Die Kunden bestellen</span></span>
<spanid="cb3-8"><ahref="#cb3-8"aria-hidden="true"tabindex="-1"></a><spanclass="co">schmalere Breiten, und aus jeder Mutterrolle werden mehrere davon geschnitten.</span></span>
<spanid="cb3-9"><ahref="#cb3-9"aria-hidden="true"tabindex="-1"></a><spanclass="co">Gesucht ist der Schnittplan, der mit den wenigsten Mutterrollen auskommt.</span></span>
<spanid="cb3-11"><ahref="#cb3-11"aria-hidden="true"tabindex="-1"></a><spanclass="co">Der naheliegende Modellansatz - "welches Stueck kommt auf welche Rolle?" -</span></span>
<spanid="cb3-12"><ahref="#cb3-12"aria-hidden="true"tabindex="-1"></a><spanclass="co">fuehrt in eine Sackgasse: Er braucht eine Binaervariable je Stueck-und-Rolle,</span></span>
<spanid="cb3-13"><ahref="#cb3-13"aria-hidden="true"tabindex="-1"></a><spanclass="co">und weil alle Rollen gleich sind, ist er hochgradig symmetrisch. Der Solver</span></span>
<spanid="cb3-14"><ahref="#cb3-14"aria-hidden="true"tabindex="-1"></a><spanclass="co">probiert dieselbe Loesung in tausend Umbenennungen durch.</span></span>
<spanid="cb3-18"><ahref="#cb3-18"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Nicht "welches Stueck auf welche Rolle",</span></span>
<spanid="cb3-19"><ahref="#cb3-19"aria-hidden="true"tabindex="-1"></a><spanclass="co"> sondern "wie oft schneide ich welches MUSTER".</span></span>
<spanid="cb3-21"><ahref="#cb3-21"aria-hidden="true"tabindex="-1"></a><spanclass="co">Damit verschwindet die Symmetrie - aber es entsteht ein neues Problem: Die Zahl</span></span>
<spanid="cb3-22"><ahref="#cb3-22"aria-hidden="true"tabindex="-1"></a><spanclass="co">der moeglichen Muster waechst kombinatorisch. Man kann sie nicht aufschreiben.</span></span>
<spanid="cb3-23"><ahref="#cb3-23"aria-hidden="true"tabindex="-1"></a><spanclass="co">Der Trick besteht darin, sie auch nicht aufzuschreiben, sondern nur die wenigen</span></span>
<spanid="cb3-24"><ahref="#cb3-24"aria-hidden="true"tabindex="-1"></a><spanclass="co">zu erzeugen, die tatsaechlich gebraucht werden - und zwar mit Hilfe der</span></span>
<spanid="cb3-25"><ahref="#cb3-25"aria-hidden="true"tabindex="-1"></a><spanclass="co">Schattenpreise aus dem Kapitel LP.</span></span>
<spanid="cb3-29"><ahref="#cb3-29"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 1. Die beiden Formulierungen desselben Problems und ihre Groesse.</span></span>
<spanid="cb3-30"><ahref="#cb3-30"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 2. Wie schnell die Musterzahl waechst - und wie wenige davon genuegen.</span></span>
<spanid="cb3-31"><ahref="#cb3-31"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 3. Die Schleife selbst: Master-LP, Dualwerte, Rucksack, neue Spalte.</span></span>
<spanid="cb3-32"><ahref="#cb3-32"aria-hidden="true"tabindex="-1"></a><spanclass="co"> 4. Wann sich der Aufwand lohnt und wann nicht. Die Antwort haengt an einer</span></span>
<spanid="cb3-33"><ahref="#cb3-33"aria-hidden="true"tabindex="-1"></a><spanclass="co"> einzigen Kennzahl der Instanz, und sie erklaert nebenbei, warum eine</span></span>
<spanid="cb3-34"><ahref="#cb3-34"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Faustregel bei manchen Zuschnittproblemen schon optimal ist.</span></span>
<spanid="cb3-46"><ahref="#cb3-46"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Zwei Instanzen mit demselben Bedarf, aber verschiedenen Breiten. Der</span></span>
<spanid="cb3-47"><ahref="#cb3-47"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Unterschied ist die eine Kennzahl, um die es in Teil 4 geht.</span></span>
<spanid="cb3-55"><ahref="#cb3-55"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Die Musterzahl ---------------------------------------------------------</span></span>
<spanid="cb3-58"><ahref="#cb3-58"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Wie viele zulaessige Schnittmuster gibt es ueberhaupt?</span></span>
<spanid="cb3-60"><ahref="#cb3-60"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Nur zum Zeigen - in einem produktiven Programm wuerde man das nie</span></span>
<spanid="cb3-61"><ahref="#cb3-61"aria-hidden="true"tabindex="-1"></a><spanclass="co"> ausrechnen, weil die Zahl bei realistischen Instanzen jede Vorstellung</span></span>
<spanid="cb3-62"><ahref="#cb3-62"aria-hidden="true"tabindex="-1"></a><spanclass="co"> sprengt. Genau das ist der Punkt.</span></span>
<spanid="cb3-67"><ahref="#cb3-67"aria-hidden="true"tabindex="-1"></a><spanclass="cf">if</span> i <spanclass="op">==</span><spanclass="bu">len</span>(breiten):</span>
<spanid="cb3-79"><ahref="#cb3-79"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- Die beiden Bausteine der Schleife --------------------------------------</span></span>
<spanid="cb3-82"><ahref="#cb3-82"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Restringiertes Master: Wie oft schneide ich jedes bekannte Muster?</span></span>
<spanid="cb3-86"><ahref="#cb3-86"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Als LP gerechnet liefert es zusaetzlich die DUALWERTE - und die sind der</span></span>
<spanid="cb3-87"><ahref="#cb3-87"aria-hidden="true"tabindex="-1"></a><spanclass="co"> eigentliche Ertrag: Der Dualwert zu Breite i sagt, was ein zusaetzliches</span></span>
<spanid="cb3-88"><ahref="#cb3-88"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Stueck dieser Breite an Rollen kostet. Es ist derselbe Schattenpreis wie</span></span>
<spanid="cb3-89"><ahref="#cb3-89"aria-hidden="true"tabindex="-1"></a><spanclass="co"> im Kapitel LP, nur dass er hier nicht berichtet, sondern weiterverarbeitet</span></span>
<spanid="cb3-92"><ahref="#cb3-92"aria-hidden="true"tabindex="-1"></a> matrix <spanclass="op">=</span><spanclass="op">-</span>np.array(muster, dtype<spanclass="op">=</span><spanclass="bu">float</span>).T <spanclass="co"># >= wird zu <= mit Minus</span></span>
<spanid="cb3-98"><ahref="#cb3-98"aria-hidden="true"tabindex="-1"></a><spanclass="cf">raise</span><spanclass="pp">SystemExit</span>(<spanclass="ss">f"Master nicht loesbar: </span><spanclass="sc">{</span>ergebnis<spanclass="sc">.</span>message<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-104"><ahref="#cb3-104"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Pricing: Gibt es ein Muster, das sich noch lohnt?</span></span>
<spanid="cb3-106"><ahref="#cb3-106"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Gesucht ist das Muster mit dem groessten Gesamtwert zu den aktuellen</span></span>
<spanid="cb3-107"><ahref="#cb3-107"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Schattenpreisen - unter der Bedingung, dass es in die Rolle passt. Das ist</span></span>
<spanid="cb3-108"><ahref="#cb3-108"aria-hidden="true"tabindex="-1"></a><spanclass="co"> ein RUCKSACKPROBLEM (Kapitel MILP, Rucksack.py): Preise sind der Nutzen,</span></span>
<spanid="cb3-109"><ahref="#cb3-109"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Breiten das Gewicht, die Rollenbreite die Kapazitaet. Hier mit dynamischer</span></span>
<spanid="cb3-110"><ahref="#cb3-110"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Programmierung geloest, weil die Breiten ganzzahlig sind.</span></span>
<spanid="cb3-112"><ahref="#cb3-112"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Bei Gleichstand wird bewusst der KLEINSTE Index bevorzugt (>-Vergleich mit</span></span>
<spanid="cb3-113"><ahref="#cb3-113"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Toleranz statt >=). Sonst haengt das erzeugte Muster von der Reihenfolge</span></span>
<spanid="cb3-114"><ahref="#cb3-114"aria-hidden="true"tabindex="-1"></a><spanclass="co"> der Gleitkommaoperationen ab, und das Programm liefert von Lauf zu Lauf</span></span>
<spanid="cb3-115"><ahref="#cb3-115"aria-hidden="true"tabindex="-1"></a><spanclass="co"> verschiedene Ausgaben.</span></span>
<spanid="cb3-119"><ahref="#cb3-119"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> platz <spanclass="kw">in</span><spanclass="bu">range</span>(rollenbreite <spanclass="op">+</span><spanclass="dv">1</span>):</span>
<spanid="cb3-120"><ahref="#cb3-120"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i, breite <spanclass="kw">in</span><spanclass="bu">enumerate</span>(breiten):</span>
<spanid="cb3-135"><ahref="#cb3-135"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Die Schleife. Startbasis: je ein Muster mit nur einer Breite.</span></span>
<spanid="cb3-137"><ahref="#cb3-137"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Abbruch, wenn kein Muster mehr einen Wert ueber 1 hat: Eine zusaetzliche</span></span>
<spanid="cb3-138"><ahref="#cb3-138"aria-hidden="true"tabindex="-1"></a><spanclass="co"> Rolle kostet 1, also lohnt sich ein neues Muster nur, wenn es zu den</span></span>
<spanid="cb3-139"><ahref="#cb3-139"aria-hidden="true"tabindex="-1"></a><spanclass="co"> aktuellen Preisen mehr als 1 wert ist. Das ist das Kriterium der</span></span>
<spanid="cb3-144"><ahref="#cb3-144"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> i <spanclass="kw">in</span><spanclass="bu">range</span>(anzahl):</span>
<spanid="cb3-158"><ahref="#cb3-158"aria-hidden="true"tabindex="-1"></a><spanclass="co">"""Die Faustregel: groesstes Stueck zuerst, auf die erste passende Rolle."""</span></span>
<spanid="cb3-184"><ahref="#cb3-184"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" SPALTENGENERIERUNG: NUR DIE MUSTER ERZEUGEN, DIE MAN BRAUCHT"</span>)</span>
<spanid="cb3-191"><ahref="#cb3-191"aria-hidden="true"tabindex="-1"></a><spanclass="co"># Wie viele Rollen braucht man hoechstens? Die Faustregel liefert eine</span></span>
<spanid="cb3-192"><ahref="#cb3-192"aria-hidden="true"tabindex="-1"></a><spanclass="co"># brauchbare Obergrenze - mehr Rollen als das wird niemand benoetigen.</span></span>
<spanid="cb3-197"><ahref="#cb3-197"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'Stueck -> Rolle (naheliegend)'</span><spanclass="sc">:<34}</span><spanclass="ss">"</span></span>
<spanid="cb3-199"><ahref="#cb3-199"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'wie oft welches Muster'</span><spanclass="sc">:<34}</span><spanclass="ss">"</span></span>
<spanid="cb3-200"><ahref="#cb3-200"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'eine je Muster'</span><spanclass="sc">:>16}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'nein'</span><spanclass="sc">:>16}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-201"><ahref="#cb3-201"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Der naheliegende Ansatz braucht eine Variable je Stueck und Rolle:"</span>)</span>
<spanid="cb3-202"><ahref="#cb3-202"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>stuecke<spanclass="sc">}</span><spanclass="ss"> Zuschnitte x </span><spanclass="sc">{</span>rollen_obergrenze<spanclass="sc">}</span><spanclass="ss"> Rollen (Obergrenze aus der Faustregel)"</span>)</span>
<spanid="cb3-204"><ahref="#cb3-204"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st"> Schlimmer als die Zahl ist die Symmetrie: Alle Mutterrollen sind"</span>)</span>
<spanid="cb3-205"><ahref="#cb3-205"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" gleich, also beschreibt jede Loesung dieselbe Schnittvorschrift in"</span>)</span>
<spanid="cb3-206"><ahref="#cb3-206"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" unzaehligen Umbenennungen. Branch-and-Bound probiert sie einzeln"</span>)</span>
<spanid="cb3-207"><ahref="#cb3-207"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" durch und kommt nicht voran - der Suchbaum waechst, ohne dass sich"</span>)</span>
<spanid="cb3-208"><ahref="#cb3-208"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" die Schranke bewegt."</span>)</span>
<spanid="cb3-209"><ahref="#cb3-209"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st"> Das Mustermodell hat je Muster genau eine Variable und kennt keine"</span>)</span>
<spanid="cb3-210"><ahref="#cb3-210"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" einzelnen Rollen mehr. Damit ist die Symmetrie weg. Sein Problem ist"</span>)</span>
<spanid="cb3-211"><ahref="#cb3-211"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" ein anderes - und zwar das folgende."</span>)</span>
<spanid="cb3-215"><ahref="#cb3-215"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"2. Warum man die Muster nicht aufschreiben kann</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb3-221"><ahref="#cb3-221"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> fall <spanclass="kw">in</span> (gross, schmal):</span>
<spanid="cb3-226"><ahref="#cb3-226"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Schon das Halbieren der Breiten laesst die Musterzahl von "</span></span>
<spanid="cb3-229"><ahref="#cb3-229"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"</span><spanclass="sc">{</span>schmal[<spanclass="st">'muster_gesamt'</span>] <spanclass="op">/</span> gross[<spanclass="st">'muster_gesamt'</span>]<spanclass="sc">:.0f}</span><spanclass="ss">. Bei einer echten "</span></span>
<spanid="cb3-231"><ahref="#cb3-231"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" vierzig Breiten und Millimeterschritten sind es mehr, als sich"</span>)</span>
<spanid="cb3-233"><ahref="#cb3-233"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Gebraucht werden davon </span><spanclass="sc">{</span>schmal[<spanclass="st">'muster_erzeugt'</span>]<spanclass="sc">}</span><spanclass="ss"> - "</span></span>
<spanid="cb3-234"><ahref="#cb3-234"aria-hidden="true"tabindex="-1"></a><spanclass="ss">f"</span><spanclass="sc">{</span>schmal[<spanclass="st">'muster_erzeugt'</span>] <spanclass="op">/</span> schmal[<spanclass="st">'muster_gesamt'</span>]<spanclass="sc">:.2%}</span><spanclass="ss"> der Gesamtzahl."</span>)</span>
<spanid="cb3-238"><ahref="#cb3-238"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"3. Die Schleife an der breiten Instanz</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb3-239"><ahref="#cb3-239"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Startbasis: </span><spanclass="sc">{</span><spanclass="bu">len</span>(BREIT)<spanclass="sc">}</span><spanclass="ss"> triviale Muster (je Rolle nur eine Breite)"</span>)</span>
<spanid="cb3-240"><ahref="#cb3-240"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Runden bis kein Muster mehr lohnt: </span><spanclass="sc">{</span>gross[<spanclass="st">'runden'</span>]<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-241"><ahref="#cb3-241"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f" Muster am Ende: </span><spanclass="sc">{</span>gross[<spanclass="st">'muster_erzeugt'</span>]<spanclass="sc">}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-243"><ahref="#cb3-243"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Die LP-Schranke ist eine ZUSAGE: Weniger als "</span></span>
<spanid="cb3-245"><ahref="#cb3-245"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" nicht moeglich - unabhaengig davon, wie clever man weiterschneidet."</span>)</span>
<spanid="cb3-246"><ahref="#cb3-246"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Genau diese Aussage fehlt einer Heuristik (Kapitel Metaheuristiken)."</span>)</span>
<spanid="cb3-248"><ahref="#cb3-248"aria-hidden="true"tabindex="-1"></a><spanclass="co"># --- 4. Wann es sich lohnt --------------------------------------------</span></span>
<spanid="cb3-250"><ahref="#cb3-250"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"4. Wann sich der Aufwand lohnt - und wann nicht</span><spanclass="ch">\n</span><spanclass="st">"</span>)</span>
<spanid="cb3-254"><ahref="#cb3-254"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> fall <spanclass="kw">in</span> (gross, schmal):</span>
<spanid="cb3-260"><ahref="#cb3-260"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Bei den breiten Zuschnitten spart die Spaltengenerierung "</span></span>
<spanid="cb3-262"><ahref="#cb3-262"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span>gross[<spanclass="st">'first_fit'</span>]<spanclass="sc">}</span><spanclass="ss"> Rollen. Bei den schmalen spart sie NICHTS - dort ist die"</span>)</span>
<spanid="cb3-263"><ahref="#cb3-263"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Faustregel bereits optimal."</span>)</span>
<spanid="cb3-264"><ahref="#cb3-264"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="ch">\n</span><spanclass="ss"> Der Unterschied haengt an einer einzigen Kennzahl:</span><spanclass="ch">\n</span><spanclass="ss">"</span>)</span>
<spanid="cb3-265"><ahref="#cb3-265"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="ss">f"</span><spanclass="sc">{</span><spanclass="st">'Instanz'</span><spanclass="sc">:<36}</span><spanclass="ss"></span><spanclass="sc">{</span><spanclass="st">'Stuecke je Rolle (etwa)'</span><spanclass="sc">:>24}</span><spanclass="ss">"</span>)</span>
<spanid="cb3-267"><ahref="#cb3-267"aria-hidden="true"tabindex="-1"></a><spanclass="cf">for</span> fall <spanclass="kw">in</span> (gross, schmal):</span>
<spanid="cb3-270"><ahref="#cb3-270"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"</span><spanclass="ch">\n</span><spanclass="st"> Passen nur zwei bis drei Stuecke auf eine Rolle, entscheidet jede"</span>)</span>
<spanid="cb3-271"><ahref="#cb3-271"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" einzelne Zuordnung viel, und eine kurzsichtige Regel verschenkt"</span>)</span>
<spanid="cb3-272"><ahref="#cb3-272"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" ganze Rollen. Passen sechs oder mehr darauf, gleichen sich die"</span>)</span>
<spanid="cb3-273"><ahref="#cb3-273"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Fehler aus - die Reste sind klein gegen die Rollenbreite, und die"</span>)</span>
<spanid="cb3-274"><ahref="#cb3-274"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Faustregel trifft es fast immer."</span>)</span>
<spanid="cb3-277"><ahref="#cb3-277"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" WAS MAN DARAUS MITNIMMT"</span>)</span>
<spanid="cb3-279"><ahref="#cb3-279"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"1. Der Perspektivwechsel ist die eigentliche Arbeit: nicht 'welches"</span>)</span>
<spanid="cb3-280"><ahref="#cb3-280"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Stueck wohin', sondern 'wie oft welches Muster'. Damit verschwindet"</span>)</span>
<spanid="cb3-281"><ahref="#cb3-281"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" die Symmetrie, die das naheliegende Modell unloesbar macht."</span>)</span>
<spanid="cb3-282"><ahref="#cb3-282"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"2. Der Preis dafuer ist eine unaufschreibbare Zahl von Variablen. Die"</span>)</span>
<spanid="cb3-283"><ahref="#cb3-283"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Spaltengenerierung zahlt ihn nicht, sondern erzeugt nur die wenigen"</span>)</span>
<spanid="cb3-284"><ahref="#cb3-284"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Spalten, die die Dualwerte als lohnend ausweisen."</span>)</span>
<spanid="cb3-285"><ahref="#cb3-285"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"3. Das Teilproblem ist ein Rucksack - ein Verfahren, das die Leser"</span>)</span>
<spanid="cb3-286"><ahref="#cb3-286"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" dieses Buchs schon kennen. Neu ist allein die Schleife."</span>)</span>
<spanid="cb3-287"><ahref="#cb3-287"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">"4. Und die unbequeme Erkenntnis: Ob sich das alles lohnt, entscheidet"</span>)</span>
<spanid="cb3-288"><ahref="#cb3-288"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" die Instanz, nicht die Methode. Bei sechs Stuecken je Rolle ist die"</span>)</span>
<spanid="cb3-289"><ahref="#cb3-289"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Faustregel so gut wie das Optimum - und der Ertrag der"</span>)</span>
<spanid="cb3-290"><ahref="#cb3-290"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" Spaltengenerierung liegt dann allein in der SCHRANKE, die beweist,"</span>)</span>
<spanid="cb3-291"><ahref="#cb3-291"aria-hidden="true"tabindex="-1"></a><spanclass="bu">print</span>(<spanclass="st">" dass man aufhoeren kann zu suchen."</span>)</span>
<p>Schon das Halbieren der Breiten lässt die Musterzahl um den <strong>Faktor 150</strong> springen. Die Zahl der <em>gebrauchten</em> Muster bleibt dagegen konstant — sie steigt sogar nicht, sie sinkt leicht. Das ist der ganze Ertrag des Verfahrens: Der Aufwand hängt an der Zahl der <strong>Bedarfszeilen</strong>, nicht an der Zahl der Muster.</p>
<h3id="der-unbequeme-teil-es-lohnt-sich-nicht-immer">Der unbequeme Teil: Es lohnt sich nicht immer</h3>
<p>Bei den breiten Zuschnitten spart die Spaltengenerierung neun von 82 Rollen. Bei den schmalen spart sie <strong>nichts</strong> — dort ist First-Fit-Decreasing bereits optimal.</p>
<p>Der Unterschied hängt an einer einzigen Kennzahl:</p>
<table>
<thead>
<trclass="header">
<th>Instanz</th>
<thstyle="text-align: right;">Stücke je Rolle</th>
</tr>
</thead>
<tbody>
<trclass="odd">
<td>breite Zuschnitte</td>
<tdstyle="text-align: right;">3,0</td>
</tr>
<trclass="even">
<td>schmale Zuschnitte</td>
<tdstyle="text-align: right;">6,4</td>
</tr>
</tbody>
</table>
<p><strong>Passen nur zwei bis drei Stücke auf eine Rolle</strong>, entscheidet jede einzelne Zuordnung viel, und eine kurzsichtige Regel verschenkt ganze Rollen. <strong>Passen sechs oder mehr darauf</strong>, gleichen sich die Fehler aus: Die Reste sind klein gegen die Rollenbreite, und die Faustregel trifft es fast immer.</p>
<blockquote>
<p><strong>💡 Das erklärt einen Befund aus <ahref="metaheuristiken.html#kap-metaheuristiken">Kapitel 9</a></strong></p>
<p>Dort steht, dass Bin Packing als Aufhänger für Metaheuristiken <strong>nicht</strong> taugt, weil First-Fit in den geprüften Größen bereits optimal war. Der Grund ist jetzt benennbar: Die dortige Instanz hatte Stücke von 700 bis 2 600 mm bei 5 600 mm Rollenbreite — im Mittel gut drei je Rolle, aber mit viel kleineren Stücken durchsetzt, so dass die Reste sich auffüllen ließen.</p>
<p>Die brauchbare Faustregel lautet also nicht „Bin Packing ist einfach”, sondern: <strong>Je weniger Stücke auf einen Behälter passen, desto mehr ist mit exakter Optimierung zu holen.</strong> Das ist eine Zahl, die man vor dem Projekt ausrechnen kann.</p>
</blockquote>
<p>Und selbst dort, wo die Ersparnis null ist, liefert das Verfahren etwas, das die Faustregel nicht kann: die <strong>Schranke 33,60</strong>. Sie beweist, dass 34 Rollen das Minimum wären und 35 höchstens eine daneben liegen — also dass man aufhören kann zu suchen.</p>
<p><strong>Aufgabe 10.1 ⭐ — Das Abbruchkriterium.</strong> Warum lautet die Schwelle beim Pricing genau <code>1</code> und nicht <code>0</code>? Woher kommt die Eins?</p>
<p><strong>Aufgabe 10.2 ⭐ — Die Startbasis.</strong> Das Programm startet mit Mustern, die je nur eine Breite enthalten. Warum ist das immer zulässig, und warum wäre eine leere Startmenge ein Problem?</p>
<p><strong>Aufgabe 10.3 ⭐⭐ — Die Kennzahl prüfen.</strong> Erzeugen Sie Instanzen mit 2, 4, 8 und 16 Stücken je Rolle und tragen Sie die Ersparnis gegenüber First-Fit auf. Bestätigt sich der Zusammenhang aus <ahref="#sec:dekomposition-befund">Abschnitt 10.6</a>?</p>
<p><strong>Aufgabe 10.4 ⭐⭐ — Dienstplanung.</strong> Formulieren Sie die Wochendienstplanung als Spaltengenerierung: Ein „Muster” ist ein zulässiger Wochenplan <strong>einer</strong> Person. Was ist das Teilproblem, und welche Nebenbedingungen stehen im Master, welche im Teilproblem?</p>
<p><strong>Aufgabe 10.5 ⭐⭐⭐ — Branch-and-Price.</strong> Das ganzzahlige Master über die erzeugten Spalten ist nicht garantiert optimal — es könnte Muster geben, die erst <em>nach</em> einer Verzweigung lohnend werden. Recherchieren Sie, was Branch-and-Price daran ändert, und erklären Sie, warum man nicht einfach auf den erzeugten Spalten verzweigen kann.</p>
<hr/>
<h2id="sec:dekomposition-denkfehler">10.8 Finde den Denkfehler</h2>
<divclass="card card-denkfehler">
<blockquote>
<p><strong>🐛 „Die LP-Lösung sagt 72,92 — also runden wir auf”</strong></p>
<p>Ein Kollege hat die Spaltengenerierung implementiert und ist zufrieden:</p>
<blockquote>
<p><em>„Das Master-LP liefert 72,92 Rollen, verteilt auf 13 Muster. Halbe Rollen kann man > nicht schneiden, also runde ich jedes Muster auf die nächste ganze Zahl auf. Damit sind > alle Bestellungen sicher gedeckt — aufrunden kann ja nur zu viel liefern, nie zu wenig. > Ergebnis: ein zulässiger Schnittplan, und die Schranke sagt mir, dass ich höchstens eine > Rolle daneben liege.”</em></p>
</blockquote>
<p>Der erste Teil stimmt: Aufrunden liefert tatsächlich einen zulässigen Plan. Der zweite Satz über die Schranke ist der Fehler — und er ist teuer.</p>
<p><strong>Wie viele Rollen kostet das Aufrunden hier wirklich, und was hätte der Kollege stattdessen tun müssen?</strong></p>
<p>Ein Hinweis: Er hat alles, was er dafür braucht, bereits vorliegen.</p>
<p><strong>1. Warum ist das Mustermodell dem Zuordnungsmodell überlegen?</strong> a) Weil es weniger Nebenbedingungen hat. b) Weil es keine einzelnen Rollen kennt und damit die Symmetrie verschwindet. c) Weil Musterprobleme immer ganzzahlige LP-Lösungen haben.</p>
<p><strong>2. Was ist das Pricing-Teilproblem beim Zuschnitt?</strong> a) Ein zweites LP über dieselben Variablen. b) Ein Rucksackproblem: Welche Stücke passen in eine Rolle und sind zu den aktuellen Schattenpreisen am meisten wert? c) Eine Heuristik, die neue Muster zufällig erzeugt.</p>
<p><strong>3. Bei der schmalen Instanz spart die Spaltengenerierung null Rollen gegenüber First-Fit. War der Aufwand umsonst?</strong> a) Ja — wo nichts gespart wird, hat sich das Verfahren nicht gelohnt. b) Nein — sie liefert die untere Schranke und damit den Beweis, dass die Faustregel höchstens eine Rolle danebenliegt. c) Nein — sie wird bei der nächsten Instanz mehr sparen.</p>
<li>Die folgenreichste Entscheidung an einem Modell ist, <strong>worüber die Variablen laufen</strong>. Beim Zuschnitt schlägt „wie oft welches Muster” das naheliegende „welches Stück auf welche Rolle” — weil damit die <strong>Symmetrie</strong> verschwindet, an der Branch-and-Bound scheitert.</li>
<li>Der Preis dafür ist eine Variablenmenge, die man nicht aufschreiben kann. Die <strong>Spaltengenerierung</strong> zahlt ihn nicht: Sie erzeugt nur die Muster, die die <strong>Dualwerte</strong> als lohnend ausweisen — im Beispiel 28 von 46 408.</li>
<li>Das Abbruchkriterium sind die <strong>reduzierten Kosten</strong>: Solange ein Muster zu den aktuellen Schattenpreisen mehr als eine Rolle wert ist, lohnt es sich. Danach ist die Lösung beweisbar optimal — auch über die nie erzeugten Muster.</li>
<li>Das <strong>Teilproblem ist ein Rucksack</strong>. Neu ist allein die Schleife.</li>
<li><strong>Ob sich das lohnt, entscheidet die Instanz.</strong> Bei drei Stücken je Rolle bringt das Verfahren 11 %, bei sechs nichts. Diese Kennzahl lässt sich vor dem Projekt ausrechnen.</li>
<li>Auch wo es nichts spart, liefert es die <strong>Schranke</strong> — und damit die Erlaubnis, aufzuhören.</li>
<navclass="prev-next"><aclass="prev-next-knopf prev-next-prev"href="metaheuristiken.html"><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a><aclass="prev-next-knopf prev-next-next"href="synthese-kernverfahren.html"><span><small>Weiter</small>Synthese Teil II</span><svgclass="icon"aria-hidden="true"><usehref="#icon-chevron-right"></use></svg></a></nav>