Sechs Dinge, alle fuer das jetzt oeffentliche Repository. LIZENZEN. LICENSE traegt die MIT-Lizenz fuer den Programmcode (alle .py, Notebooks, Dockerfile, pyproject.toml), LICENSE-TEXT.md die CC BY-SA 4.0 fuer Buchtext, PDF, Website und Diagramme. Beide Dateien nennen ausdruecklich, was sie abdecken und was nicht - ein Codeblock im Buchtext bleibt Programmcode und steht unter MIT. Vorher hatte das Repository gar keine Lizenz, womit standardmaessig "alle Rechte vorbehalten" galt und niemand die 76 Programme haette weiterverwenden duerfen. COLAB. COLAB_BASIS_URL steht jetzt auf "". Der Platzhalter zeigte auf GitHub, das Repository liegt auf einer eigenen Forgejo-Instanz - und Colab oeffnet Notebooks NUR aus GitHub, Google Drive oder einem Upload. Die URL-Form colab.research.google.com/github/... ist fest auf GitHub verdrahtet; eine selbstgehostete Adresse dort einzusetzen ergaebe keinen Link zum eigenen Server, sondern einen toten GitHub-Link. Der Kommentar im Quelltext ging von GitHub aus und war damit selbst irrefuehrend; er ist ersetzt. Verloren geht nichts: Die 25 Notebooks liegen neben der Website und bekommen einen echten Download-Link, jetzt mit dem Hinweis, was man damit tut - "herunterladen und in Jupyter oeffnen, in Colab hochladen oder mit dem Kurs-Image starten". JUPYTERLAB IM KURS-IMAGE. Neue pyproject-Gruppe [notebook] mit jupyterlab, die Notebooks werden ins Image kopiert, und ein kleiner Startbefehl macht beide Betriebsarten ohne --entrypoint moeglich: ohne Argument JupyterLab, mit Argument ein einzelnes Programm. Gebaut und geprueft - Rucksack.py laeuft, JupyterLab antwortet mit HTTP 200 und zeigt alle 25 Notebooks. Image 1,31 -> 1,46 GB. Es laeuft ohne Token, deshalb im README die Portfreigabe an 127.0.0.1 gebunden. README KOMPLETT NEU. Es war die Bau-Anleitung eines privaten Verzeichnisses und ist jetzt die Visitenkarte eines oeffentlichen Repositorys: was das Buch ist, wo man es liest, drei Wege die Beispiele auszufuehren (Container, schlanke Installation, alles auf einmal), was hier liegt, wie man baut, die Colab-Frage, die Lizenzen und wie man mitwirkt. Alle relativen Links geprueft: 0 tot. .gitattributes. Ohne die Datei entschied core.autocrlf des jeweiligen Rechners, was beim Klonen passiert - ein Windows-Leser bekam CRLF-Rauschen in jedem Diff. Jetzt: im Repository immer LF, im Arbeitsverzeichnis passend zum System, Binaerdateien ausdruecklich ausgenommen. Der Bestand war bereits sauber (git add --renormalize aendert null Dateien). Zusaetzlich sind die erzeugten Verzeichnisse als linguist-generated markiert, sonst zaehlt die Sprachstatistik das Repository als HTML-Projekt. PROGRESS.md: Remote-Repository als erledigt markiert, der Colab-Befund festgehalten. CLAUDE.md um Veroeffentlichung, Lizenzen und die neuen Dateien ergaenzt. Geprueft: --check ohne Fehler, 0 tote README-Links, 76 Programme unveraendert, 33 pytest-Tests, pyproject baut mit acht Gruppen, PDF 759 Seiten (eine weniger - die Colab-Zeile entfaellt in 25 Kapiteln). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
839 lines
104 KiB
HTML
Generated
839 lines
104 KiB
HTML
Generated
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten · Optimierte Entscheidungsfindung mit Python</title>
|
||
<script>
|
||
(function () {
|
||
try {
|
||
var t = localStorage.getItem("or-theme");
|
||
if (t) document.documentElement.setAttribute("data-theme", t);
|
||
} catch (e) {}
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="assets/highlight.css" />
|
||
<link rel="stylesheet" href="katex/katex.min.css" />
|
||
<script defer="" src="katex/katex.min.js"></script>
|
||
<script>document.addEventListener("DOMContentLoaded", function () {
|
||
var mathElements = document.getElementsByClassName("math");
|
||
var macros = [];
|
||
for (var i = 0; i < mathElements.length; i++) {
|
||
var texText = mathElements[i].firstChild;
|
||
if (mathElements[i].tagName == "SPAN") {
|
||
katex.render(texText.data, mathElements[i], {
|
||
displayMode: mathElements[i].classList.contains('display'),
|
||
throwOnError: false,
|
||
macros: macros,
|
||
fleqn: false
|
||
});
|
||
}}
|
||
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
|
||
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
|
||
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
|
||
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
|
||
if (location.hash) {
|
||
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
|
||
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
|
||
}
|
||
});
|
||
</script>
|
||
<link rel="stylesheet" href="assets/site.css" />
|
||
</head>
|
||
<body>
|
||
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
|
||
</symbol>
|
||
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
|
||
</symbol>
|
||
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<circle cx="12" cy="12" r="4.5"/>
|
||
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
|
||
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
|
||
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
|
||
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
|
||
</symbol>
|
||
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
|
||
</symbol>
|
||
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="15 4 7 12 15 20"/>
|
||
</symbol>
|
||
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="9 4 17 12 9 20"/>
|
||
</symbol>
|
||
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="4 13 9.5 18.5 20 6"/>
|
||
</symbol>
|
||
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
|
||
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
|
||
</symbol>
|
||
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
|
||
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
|
||
</symbol>
|
||
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||
</symbol>
|
||
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
|
||
</symbol></svg>
|
||
<header class="site-header">
|
||
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
|
||
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
|
||
<div class="site-search">
|
||
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
|
||
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
|
||
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
|
||
</div>
|
||
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
|
||
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
|
||
</button>
|
||
</header>
|
||
<div class="site-body">
|
||
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
|
||
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><li data-kapitel="synthese-grundlagen.html"><a href="synthese-grundlagen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><details class="sidebar-gruppe" open><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html" class="aktiv"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><li data-kapitel="synthese-kernverfahren.html"><a href="synthese-kernverfahren.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</summary><ul><li data-kapitel="qp-nlp.html"><a href="qp-nlp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a></li><li data-kapitel="unsicherheit.html"><a href="unsicherheit.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a></li><li data-kapitel="dynamische-programmierung.html"><a href="dynamische-programmierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span></a></li><li data-kapitel="mehrziel.html"><a href="mehrziel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span></a></li><li data-kapitel="prognose.html"><a href="prognose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung</span></a></li><li data-kapitel="synthese-nichtlinear.html"><a href="synthese-nichtlinear.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil III</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte</summary><ul><li data-kapitel="bruecke.html"><a href="bruecke.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten</span></a></li><li data-kapitel="supplychain.html"><a href="supplychain.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit</span></a></li><li data-kapitel="finanzdaten.html"><a href="finanzdaten.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage</span></a></li><li data-kapitel="markowitz.html"><a href="markowitz.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 19: Die moderne Portfoliotheorie nach Markowitz</span></a></li><li data-kapitel="cvar.html"><a href="cvar.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 20: Tail-Risiko, CVaR und Transaktionskosten</span></a></li><li data-kapitel="handelsmaschine.html"><a href="handelsmaschine.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 21: Die vollständige quantitative Handelsmaschine</span></a></li><li data-kapitel="synthese-anwendungen.html"><a href="synthese-anwendungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil IV</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil V: Praxis</summary><ul><li data-kapitel="praxisfallen.html"><a href="praxisfallen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 22: Praxisfallen und der Weg zum produktiven Einsatz</span></a></li><li data-kapitel="testing.html"><a href="testing.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 23: Testen, Messen, Ausliefern</span></a></li><li data-kapitel="synthese-praxis.html"><a href="synthese-praxis.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil V</span></a></li><li data-kapitel="projektwerkstatt.html"><a href="projektwerkstatt.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Projektwerkstatt</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Anhänge</summary><ul><li data-kapitel="anhang-loesungen.html"><a href="anhang-loesungen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang A: Lösungen zu allen Übungsaufgaben</span></a></li><li data-kapitel="anhang-modellierungsmuster.html"><a href="anhang-modellierungsmuster.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang B: Katalog der Modellierungsmuster</span></a></li><li data-kapitel="anhang-fehlerdiagnose.html"><a href="anhang-fehlerdiagnose.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang C: Fehlerdiagnose-Handbuch</span></a></li><li data-kapitel="anhang-spickzettel.html"><a href="anhang-spickzettel.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang D: Spickzettel der Solver</span></a></li><li data-kapitel="anhang-glossar-literatur.html"><a href="anhang-glossar-literatur.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Anhang E: Glossar und Literatur</span></a></li></ul></details><ul class="sidebar-extra"><li><a href="programme.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> Beispielprogramme</a></li><li><a href="stichwortverzeichnis.html"><svg class="icon" aria-hidden="true"><use href="#icon-search"></use></svg> Stichwortverzeichnis</a></li><li><a href="gesamtdokument.html"><svg class="icon" aria-hidden="true"><use href="#icon-external-link"></use></svg> Gesamtdokument (eine Seite)</a></li><li><a href="Operations_Research_mit_Python_Version_04.pdf" download><svg class="icon" aria-hidden="true"><use href="#icon-download"></use></svg> Download als PDF</a></li></ul></div></nav>
|
||
<main class="content">
|
||
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> › <span>Teil II</span> › <span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></nav>
|
||
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="metaheuristiken.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a><a class="prev-next-knopf prev-next-next" href="synthese-kernverfahren.html"><span><small>Weiter</small>Synthese Teil II</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
|
||
<article>
|
||
<h1 id="kap-dekomposition">Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</h1>
|
||
<div class="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 <a href="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> <a href="lp.html#sec:lp-dualitaet-und-schattenpreise">Abschnitt 5.6</a> (Dualwerte) und <a href="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>Programme:</strong><br />
|
||
<code>Spaltengenerierung.py</code></p>
|
||
<p><strong>Notebook:</strong> <a href="Notebooks_04/dekomposition.ipynb">dekomposition.ipynb</a> — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten</p>
|
||
</blockquote>
|
||
</div>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-schnellstart">10.1 In 5 Minuten gelöst</h2>
|
||
<div class="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>
|
||
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> itertools</span>
|
||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>ROLLE <span class="op">=</span> <span class="dv">1000</span> <span class="co"># mm Mutterrolle</span></span>
|
||
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>breiten, bedarf <span class="op">=</span> [<span class="dv">420</span>, <span class="dv">310</span>, <span class="dv">250</span>], [<span class="dv">9</span>, <span class="dv">12</span>, <span class="dv">14</span>]</span>
|
||
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Alle Schnittmuster aufzaehlen, die in die Rolle passen</span></span>
|
||
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>muster <span class="op">=</span> [m <span class="cf">for</span> m <span class="kw">in</span> itertools.product(<span class="op">*</span>[<span class="bu">range</span>(ROLLE <span class="op">//</span> b <span class="op">+</span> <span class="dv">1</span>) <span class="cf">for</span> b <span class="kw">in</span> breiten])</span>
|
||
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">sum</span>(a <span class="op">*</span> b <span class="cf">for</span> a, b <span class="kw">in</span> <span class="bu">zip</span>(m, breiten)) <span class="op"><=</span> ROLLE <span class="kw">and</span> <span class="bu">sum</span>(m) <span class="op">></span> <span class="dv">0</span>]</span>
|
||
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Wie oft schneide ich welches Muster? min sum(x) u.d.N. A x >= bedarf</span></span>
|
||
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>loesung <span class="op">=</span> linprog(np.ones(<span class="bu">len</span>(muster)), A_ub<span class="op">=-</span>np.array(muster, <span class="bu">float</span>).T,</span>
|
||
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> b_ub<span class="op">=-</span>np.array(bedarf, <span class="bu">float</span>), bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="va">None</span>),</span>
|
||
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> integrality<span class="op">=</span><span class="dv">1</span>, method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span><span class="bu">len</span>(muster)<span class="sc">}</span><span class="ss"> zulaessige Muster, </span><span class="sc">{</span><span class="bu">int</span>(<span class="bu">round</span>(loesung.fun))<span class="sc">}</span><span class="ss"> Rollen noetig:"</span>)</span>
|
||
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> anzahl, m <span class="kw">in</span> <span class="bu">zip</span>(np.<span class="bu">round</span>(loesung.x).astype(<span class="bu">int</span>), muster):</span>
|
||
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> anzahl:</span>
|
||
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> ROLLE <span class="op">-</span> <span class="bu">sum</span>(a <span class="op">*</span> b <span class="cf">for</span> a, b <span class="kw">in</span> <span class="bu">zip</span>(m, breiten))</span>
|
||
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>anzahl<span class="sc">:2d}</span><span class="ss"> x "</span> <span class="op">+</span> <span class="st">" "</span>.join(<span class="ss">f"</span><span class="sc">{</span>a<span class="sc">}</span><span class="ss">x</span><span class="sc">{</span>b<span class="sc">}</span><span class="ss">mm"</span> <span class="cf">for</span> a, b <span class="kw">in</span> <span class="bu">zip</span>(m, breiten))</span>
|
||
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="ss">f" Verschnitt </span><span class="sc">{</span>rest<span class="sc">}</span><span class="ss"> mm"</span>)</span></code></pre></div>
|
||
<p><strong>Ausgabe:</strong></p>
|
||
<pre><code>16 zulaessige Muster, 12 Rollen noetig:
|
||
2 x 0x420mm 0x310mm 4x250mm Verschnitt 0 mm
|
||
1 x 0x420mm 3x310mm 0x250mm Verschnitt 70 mm
|
||
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>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-lernziele">10.2 Lernziele</h2>
|
||
<p>Nach diesem Kapitel können Sie …</p>
|
||
<ol type="1">
|
||
<li>… erklären, warum das naheliegende Zuordnungsmodell an <strong>Symmetrie</strong> scheitert.</li>
|
||
<li>… ein Problem in <strong>Master</strong> und <strong>Pricing-Teilproblem</strong> zerlegen.</li>
|
||
<li>… die Schleife schreiben: Master-LP lösen, Dualwerte auslesen, Teilproblem lösen, Spalte hinzufügen.</li>
|
||
<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 />
|
||
<h2 id="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 <span class="math inline">z_{jr}</span> für „Stück <span class="math inline">j</span> liegt auf Rolle <span class="math inline">r</span>“, dazu <span class="math inline">y_r</span> für „Rolle <span class="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 <a href="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><span class="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>
|
||
<div class="card card-formel">
|
||
<blockquote>
|
||
<p><strong>🔤 Formel-Übersetzer</strong></p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Mathematik</th>
|
||
<th>Alltagssprache</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><span class="math inline">P</span></td>
|
||
<td>die Menge aller zulässigen Schnittmuster</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><span class="math inline">a_{ip}</span></td>
|
||
<td>„Wie viele Stücke der Breite <span class="math inline">i</span> liefert Muster <span class="math inline">p</span>?”</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><span class="math inline">x_p</span></td>
|
||
<td>„Wie oft schneide ich Muster <span class="math inline">p</span>?” — die einzige Entscheidung</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><span class="math inline">d_i</span></td>
|
||
<td>die bestellte Stückzahl der Breite <span class="math inline">i</span></td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><span class="math inline">\sum_p x_p</span></td>
|
||
<td>die Zahl der verbrauchten Mutterrollen</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<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 <span class="math inline">P</span>, und das ist der Haken.</p>
|
||
</blockquote>
|
||
</div>
|
||
<hr />
|
||
<h2 id="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 <span class="math inline">\pi_i</span> zur Bedarfszeile <span class="math inline">i</span> ist genau der Schattenpreis aus <a href="lp.html#sec:lp-dualitaet-und-schattenpreise">Abschnitt 5.6</a>: Er sagt, wie viele Rollen ein zusätzliches Stück der Breite <span class="math inline">i</span> kostet.</p>
|
||
<p>Ein neues Muster <span class="math inline">a</span> lohnt sich, wenn seine <strong>reduzierten Kosten</strong> negativ sind:</p>
|
||
<p><span class="math display">
|
||
1 - \sum_i \pi_i\, a_i < 0
|
||
\qquad\Longleftrightarrow\qquad
|
||
\sum_i \pi_i\, a_i > 1
|
||
</span></p>
|
||
<div class="card card-formel">
|
||
<blockquote>
|
||
<p><strong>🔤 Formel-Übersetzer</strong></p>
|
||
<table>
|
||
<colgroup>
|
||
<col style="width: 50%" />
|
||
<col style="width: 50%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Mathematik</th>
|
||
<th>Alltagssprache</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>die <span class="math inline">1</span></td>
|
||
<td>„Ein zusätzliches Muster zu schneiden kostet eine Rolle.”</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><span class="math inline">\sum_i \pi_i a_i</span></td>
|
||
<td>„So viel ist das, was dabei herauskommt, zu den aktuellen Schattenpreisen wert.”</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><span class="math inline">> 1</span></td>
|
||
<td>„Es bringt mehr, als es kostet — dieses Muster nehmen wir dazu.”</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><span class="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>
|
||
<table>
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Rucksack (<a href="milp.html#kap-milp">Kapitel 6</a>)</th>
|
||
<th>hier</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>Nutzen eines Gegenstands</td>
|
||
<td>Schattenpreis <span class="math inline">\pi_i</span> der Breite</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>Gewicht</td>
|
||
<td>Breite in mm</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>Kapazität des Rucksacks</td>
|
||
<td>Rollenbreite</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Das Teilproblem ist also ein Verfahren, das die Leser dieses Buchs schon kennen. <strong>Neu ist allein die Schleife.</strong></p>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-programm">10.5 Das Programm</h2>
|
||
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
|
||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Spaltengenerierung.py</span></span>
|
||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Dekomposition: Das Modell umbauen, statt die Loesung zu raten.</span></span>
|
||
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">Eine Papierfabrik liefert Rollen von 5.600 mm Breite. Die Kunden bestellen</span></span>
|
||
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="co">schmalere Breiten, und aus jeder Mutterrolle werden mehrere davon geschnitten.</span></span>
|
||
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">Gesucht ist der Schnittplan, der mit den wenigsten Mutterrollen auskommt.</span></span>
|
||
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="co">Der naheliegende Modellansatz - "welches Stueck kommt auf welche Rolle?" -</span></span>
|
||
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="co">fuehrt in eine Sackgasse: Er braucht eine Binaervariable je Stueck-und-Rolle,</span></span>
|
||
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="co">und weil alle Rollen gleich sind, ist er hochgradig symmetrisch. Der Solver</span></span>
|
||
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">probiert dieselbe Loesung in tausend Umbenennungen durch.</span></span>
|
||
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="co">Die Spaltengenerierung dreht die Frage um:</span></span>
|
||
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="co"> Nicht "welches Stueck auf welche Rolle",</span></span>
|
||
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="co"> sondern "wie oft schneide ich welches MUSTER".</span></span>
|
||
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="co">Damit verschwindet die Symmetrie - aber es entsteht ein neues Problem: Die Zahl</span></span>
|
||
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a><span class="co">der moeglichen Muster waechst kombinatorisch. Man kann sie nicht aufschreiben.</span></span>
|
||
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a><span class="co">Der Trick besteht darin, sie auch nicht aufzuschreiben, sondern nur die wenigen</span></span>
|
||
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="co">zu erzeugen, die tatsaechlich gebraucht werden - und zwar mit Hilfe der</span></span>
|
||
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="co">Schattenpreise aus dem Kapitel LP.</span></span>
|
||
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a><span class="co">Das Programm zeigt vier Dinge:</span></span>
|
||
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="co"> 1. Die beiden Formulierungen desselben Problems und ihre Groesse.</span></span>
|
||
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a><span class="co"> 2. Wie schnell die Musterzahl waechst - und wie wenige davon genuegen.</span></span>
|
||
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a><span class="co"> 3. Die Schleife selbst: Master-LP, Dualwerte, Rucksack, neue Spalte.</span></span>
|
||
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a><span class="co"> 4. Wann sich der Aufwand lohnt und wann nicht. Die Antwort haengt an einer</span></span>
|
||
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a><span class="co"> einzigen Kennzahl der Instanz, und sie erklaert nebenbei, warum eine</span></span>
|
||
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a><span class="co"> Faustregel bei manchen Zuschnittproblemen schon optimal ist.</span></span>
|
||
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: numpy, scipy</span></span>
|
||
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
||
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
|
||
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
||
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> scipy.optimize <span class="im">import</span> linprog</span>
|
||
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a>ROLLENBREITE <span class="op">=</span> <span class="dv">5600</span></span>
|
||
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a><span class="co"># Zwei Instanzen mit demselben Bedarf, aber verschiedenen Breiten. Der</span></span>
|
||
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a><span class="co"># Unterschied ist die eine Kennzahl, um die es in Teil 4 geht.</span></span>
|
||
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a>BEDARF <span class="op">=</span> [<span class="dv">22</span>, <span class="dv">25</span>, <span class="dv">12</span>, <span class="dv">14</span>, <span class="dv">18</span>, <span class="dv">18</span>, <span class="dv">20</span>, <span class="dv">10</span>, <span class="dv">12</span>, <span class="dv">14</span>, <span class="dv">16</span>, <span class="dv">18</span>, <span class="dv">20</span>]</span>
|
||
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a>BREIT <span class="op">=</span> [<span class="dv">1380</span>, <span class="dv">1520</span>, <span class="dv">1560</span>, <span class="dv">1710</span>, <span class="dv">1820</span>, <span class="dv">1880</span>, <span class="dv">1930</span>, <span class="dv">2000</span>, <span class="dv">2050</span>, <span class="dv">2100</span>, <span class="dv">2140</span>,</span>
|
||
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a> <span class="dv">2150</span>, <span class="dv">2200</span>]</span>
|
||
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a>SCHMAL <span class="op">=</span> [<span class="dv">380</span>, <span class="dv">520</span>, <span class="dv">560</span>, <span class="dv">710</span>, <span class="dv">820</span>, <span class="dv">880</span>, <span class="dv">930</span>, <span class="dv">1000</span>, <span class="dv">1050</span>, <span class="dv">1100</span>, <span class="dv">1140</span>,</span>
|
||
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a> <span class="dv">1150</span>, <span class="dv">1200</span>]</span>
|
||
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a><span class="co"># --- Die Musterzahl ---------------------------------------------------------</span></span>
|
||
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> zaehle_muster(breiten, rollenbreite: <span class="bu">int</span> <span class="op">=</span> ROLLENBREITE) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Wie viele zulaessige Schnittmuster gibt es ueberhaupt?</span></span>
|
||
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a><span class="co"> Nur zum Zeigen - in einem produktiven Programm wuerde man das nie</span></span>
|
||
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a><span class="co"> ausrechnen, weil die Zahl bei realistischen Instanzen jede Vorstellung</span></span>
|
||
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a><span class="co"> sprengt. Genau das ist der Punkt.</span></span>
|
||
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a> speicher: <span class="bu">dict</span>[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="bu">int</span>] <span class="op">=</span> {}</span>
|
||
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> ab(i: <span class="bu">int</span>, rest: <span class="bu">int</span>) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> i <span class="op">==</span> <span class="bu">len</span>(breiten):</span>
|
||
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="dv">1</span></span>
|
||
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (i, rest) <span class="kw">in</span> speicher:</span>
|
||
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> speicher[(i, rest)]</span>
|
||
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a> summe <span class="op">=</span> <span class="bu">sum</span>(ab(i <span class="op">+</span> <span class="dv">1</span>, rest <span class="op">-</span> anzahl <span class="op">*</span> breiten[i])</span>
|
||
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> anzahl <span class="kw">in</span> <span class="bu">range</span>(rest <span class="op">//</span> breiten[i] <span class="op">+</span> <span class="dv">1</span>))</span>
|
||
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a> speicher[(i, rest)] <span class="op">=</span> summe</span>
|
||
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> summe</span>
|
||
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ab(<span class="dv">0</span>, rollenbreite)</span>
|
||
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true" tabindex="-1"></a><span class="co"># --- Die beiden Bausteine der Schleife --------------------------------------</span></span>
|
||
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_master(muster, bedarf, ganzzahlig: <span class="bu">bool</span> <span class="op">=</span> <span class="va">False</span>):</span>
|
||
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Restringiertes Master: Wie oft schneide ich jedes bekannte Muster?</span></span>
|
||
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true" tabindex="-1"></a><span class="co"> min sum_p x_p unter sum_p a_ip * x_p >= bedarf_i</span></span>
|
||
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-86"><a href="#cb3-86" aria-hidden="true" tabindex="-1"></a><span class="co"> Als LP gerechnet liefert es zusaetzlich die DUALWERTE - und die sind der</span></span>
|
||
<span id="cb3-87"><a href="#cb3-87" aria-hidden="true" tabindex="-1"></a><span class="co"> eigentliche Ertrag: Der Dualwert zu Breite i sagt, was ein zusaetzliches</span></span>
|
||
<span id="cb3-88"><a href="#cb3-88" aria-hidden="true" tabindex="-1"></a><span class="co"> Stueck dieser Breite an Rollen kostet. Es ist derselbe Schattenpreis wie</span></span>
|
||
<span id="cb3-89"><a href="#cb3-89" aria-hidden="true" tabindex="-1"></a><span class="co"> im Kapitel LP, nur dass er hier nicht berichtet, sondern weiterverarbeitet</span></span>
|
||
<span id="cb3-90"><a href="#cb3-90" aria-hidden="true" tabindex="-1"></a><span class="co"> wird.</span></span>
|
||
<span id="cb3-91"><a href="#cb3-91" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||
<span id="cb3-92"><a href="#cb3-92" aria-hidden="true" tabindex="-1"></a> matrix <span class="op">=</span> <span class="op">-</span>np.array(muster, dtype<span class="op">=</span><span class="bu">float</span>).T <span class="co"># >= wird zu <= mit Minus</span></span>
|
||
<span id="cb3-93"><a href="#cb3-93" aria-hidden="true" tabindex="-1"></a> ergebnis <span class="op">=</span> linprog(np.ones(<span class="bu">len</span>(muster)),</span>
|
||
<span id="cb3-94"><a href="#cb3-94" aria-hidden="true" tabindex="-1"></a> A_ub<span class="op">=</span>matrix, b_ub<span class="op">=-</span>np.array(bedarf, dtype<span class="op">=</span><span class="bu">float</span>),</span>
|
||
<span id="cb3-95"><a href="#cb3-95" aria-hidden="true" tabindex="-1"></a> bounds<span class="op">=</span>(<span class="dv">0</span>, <span class="va">None</span>),</span>
|
||
<span id="cb3-96"><a href="#cb3-96" aria-hidden="true" tabindex="-1"></a> integrality<span class="op">=</span>(<span class="dv">1</span> <span class="cf">if</span> ganzzahlig <span class="cf">else</span> <span class="dv">0</span>), method<span class="op">=</span><span class="st">"highs"</span>)</span>
|
||
<span id="cb3-97"><a href="#cb3-97" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> ergebnis.success:</span>
|
||
<span id="cb3-98"><a href="#cb3-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">SystemExit</span>(<span class="ss">f"Master nicht loesbar: </span><span class="sc">{</span>ergebnis<span class="sc">.</span>message<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-99"><a href="#cb3-99" aria-hidden="true" tabindex="-1"></a> dual <span class="op">=</span> <span class="va">None</span> <span class="cf">if</span> ganzzahlig <span class="cf">else</span> <span class="op">-</span>ergebnis.ineqlin.marginals</span>
|
||
<span id="cb3-100"><a href="#cb3-100" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ergebnis.fun, ergebnis.x, dual</span>
|
||
<span id="cb3-101"><a href="#cb3-101" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-102"><a href="#cb3-102" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-103"><a href="#cb3-103" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> bestes_neues_muster(dual, breiten, rollenbreite: <span class="bu">int</span> <span class="op">=</span> ROLLENBREITE):</span>
|
||
<span id="cb3-104"><a href="#cb3-104" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Pricing: Gibt es ein Muster, das sich noch lohnt?</span></span>
|
||
<span id="cb3-105"><a href="#cb3-105" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-106"><a href="#cb3-106" aria-hidden="true" tabindex="-1"></a><span class="co"> Gesucht ist das Muster mit dem groessten Gesamtwert zu den aktuellen</span></span>
|
||
<span id="cb3-107"><a href="#cb3-107" aria-hidden="true" tabindex="-1"></a><span class="co"> Schattenpreisen - unter der Bedingung, dass es in die Rolle passt. Das ist</span></span>
|
||
<span id="cb3-108"><a href="#cb3-108" aria-hidden="true" tabindex="-1"></a><span class="co"> ein RUCKSACKPROBLEM (Kapitel MILP, Rucksack.py): Preise sind der Nutzen,</span></span>
|
||
<span id="cb3-109"><a href="#cb3-109" aria-hidden="true" tabindex="-1"></a><span class="co"> Breiten das Gewicht, die Rollenbreite die Kapazitaet. Hier mit dynamischer</span></span>
|
||
<span id="cb3-110"><a href="#cb3-110" aria-hidden="true" tabindex="-1"></a><span class="co"> Programmierung geloest, weil die Breiten ganzzahlig sind.</span></span>
|
||
<span id="cb3-111"><a href="#cb3-111" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-112"><a href="#cb3-112" aria-hidden="true" tabindex="-1"></a><span class="co"> Bei Gleichstand wird bewusst der KLEINSTE Index bevorzugt (>-Vergleich mit</span></span>
|
||
<span id="cb3-113"><a href="#cb3-113" aria-hidden="true" tabindex="-1"></a><span class="co"> Toleranz statt >=). Sonst haengt das erzeugte Muster von der Reihenfolge</span></span>
|
||
<span id="cb3-114"><a href="#cb3-114" aria-hidden="true" tabindex="-1"></a><span class="co"> der Gleitkommaoperationen ab, und das Programm liefert von Lauf zu Lauf</span></span>
|
||
<span id="cb3-115"><a href="#cb3-115" aria-hidden="true" tabindex="-1"></a><span class="co"> verschiedene Ausgaben.</span></span>
|
||
<span id="cb3-116"><a href="#cb3-116" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||
<span id="cb3-117"><a href="#cb3-117" aria-hidden="true" tabindex="-1"></a> wert <span class="op">=</span> np.zeros(rollenbreite <span class="op">+</span> <span class="dv">1</span>)</span>
|
||
<span id="cb3-118"><a href="#cb3-118" aria-hidden="true" tabindex="-1"></a> herkunft <span class="op">=</span> [<span class="op">-</span><span class="dv">1</span>] <span class="op">*</span> (rollenbreite <span class="op">+</span> <span class="dv">1</span>)</span>
|
||
<span id="cb3-119"><a href="#cb3-119" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> platz <span class="kw">in</span> <span class="bu">range</span>(rollenbreite <span class="op">+</span> <span class="dv">1</span>):</span>
|
||
<span id="cb3-120"><a href="#cb3-120" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i, breite <span class="kw">in</span> <span class="bu">enumerate</span>(breiten):</span>
|
||
<span id="cb3-121"><a href="#cb3-121" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> breite <span class="op"><=</span> platz <span class="kw">and</span> wert[platz <span class="op">-</span> breite] <span class="op">+</span> dual[i] <span class="op">></span> wert[platz] <span class="op">+</span> <span class="fl">1e-9</span>:</span>
|
||
<span id="cb3-122"><a href="#cb3-122" aria-hidden="true" tabindex="-1"></a> wert[platz] <span class="op">=</span> wert[platz <span class="op">-</span> breite] <span class="op">+</span> dual[i]</span>
|
||
<span id="cb3-123"><a href="#cb3-123" aria-hidden="true" tabindex="-1"></a> herkunft[platz] <span class="op">=</span> i</span>
|
||
<span id="cb3-124"><a href="#cb3-124" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-125"><a href="#cb3-125" aria-hidden="true" tabindex="-1"></a> muster <span class="op">=</span> [<span class="dv">0</span>] <span class="op">*</span> <span class="bu">len</span>(breiten)</span>
|
||
<span id="cb3-126"><a href="#cb3-126" aria-hidden="true" tabindex="-1"></a> platz <span class="op">=</span> rollenbreite</span>
|
||
<span id="cb3-127"><a href="#cb3-127" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> herkunft[platz] <span class="op">>=</span> <span class="dv">0</span>:</span>
|
||
<span id="cb3-128"><a href="#cb3-128" aria-hidden="true" tabindex="-1"></a> i <span class="op">=</span> herkunft[platz]</span>
|
||
<span id="cb3-129"><a href="#cb3-129" aria-hidden="true" tabindex="-1"></a> muster[i] <span class="op">+=</span> <span class="dv">1</span></span>
|
||
<span id="cb3-130"><a href="#cb3-130" aria-hidden="true" tabindex="-1"></a> platz <span class="op">-=</span> breiten[i]</span>
|
||
<span id="cb3-131"><a href="#cb3-131" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(wert[rollenbreite]), muster</span>
|
||
<span id="cb3-132"><a href="#cb3-132" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-133"><a href="#cb3-133" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-134"><a href="#cb3-134" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> spaltengenerierung(breiten, bedarf, rollenbreite: <span class="bu">int</span> <span class="op">=</span> ROLLENBREITE):</span>
|
||
<span id="cb3-135"><a href="#cb3-135" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Die Schleife. Startbasis: je ein Muster mit nur einer Breite.</span></span>
|
||
<span id="cb3-136"><a href="#cb3-136" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-137"><a href="#cb3-137" aria-hidden="true" tabindex="-1"></a><span class="co"> Abbruch, wenn kein Muster mehr einen Wert ueber 1 hat: Eine zusaetzliche</span></span>
|
||
<span id="cb3-138"><a href="#cb3-138" aria-hidden="true" tabindex="-1"></a><span class="co"> Rolle kostet 1, also lohnt sich ein neues Muster nur, wenn es zu den</span></span>
|
||
<span id="cb3-139"><a href="#cb3-139" aria-hidden="true" tabindex="-1"></a><span class="co"> aktuellen Preisen mehr als 1 wert ist. Das ist das Kriterium der</span></span>
|
||
<span id="cb3-140"><a href="#cb3-140" aria-hidden="true" tabindex="-1"></a><span class="co"> reduzierten Kosten.</span></span>
|
||
<span id="cb3-141"><a href="#cb3-141" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
||
<span id="cb3-142"><a href="#cb3-142" aria-hidden="true" tabindex="-1"></a> anzahl <span class="op">=</span> <span class="bu">len</span>(breiten)</span>
|
||
<span id="cb3-143"><a href="#cb3-143" aria-hidden="true" tabindex="-1"></a> muster <span class="op">=</span> [[<span class="dv">0</span>] <span class="op">*</span> anzahl <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(anzahl)]</span>
|
||
<span id="cb3-144"><a href="#cb3-144" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(anzahl):</span>
|
||
<span id="cb3-145"><a href="#cb3-145" aria-hidden="true" tabindex="-1"></a> muster[i][i] <span class="op">=</span> rollenbreite <span class="op">//</span> breiten[i]</span>
|
||
<span id="cb3-146"><a href="#cb3-146" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-147"><a href="#cb3-147" aria-hidden="true" tabindex="-1"></a> runden <span class="op">=</span> <span class="dv">0</span></span>
|
||
<span id="cb3-148"><a href="#cb3-148" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="va">True</span>:</span>
|
||
<span id="cb3-149"><a href="#cb3-149" aria-hidden="true" tabindex="-1"></a> zielwert, _, dual <span class="op">=</span> loese_master(muster, bedarf)</span>
|
||
<span id="cb3-150"><a href="#cb3-150" aria-hidden="true" tabindex="-1"></a> wert, neu <span class="op">=</span> bestes_neues_muster(dual, breiten, rollenbreite)</span>
|
||
<span id="cb3-151"><a href="#cb3-151" aria-hidden="true" tabindex="-1"></a> runden <span class="op">+=</span> <span class="dv">1</span></span>
|
||
<span id="cb3-152"><a href="#cb3-152" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> wert <span class="op"><=</span> <span class="dv">1</span> <span class="op">+</span> <span class="fl">1e-6</span>:</span>
|
||
<span id="cb3-153"><a href="#cb3-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> muster, zielwert, runden</span>
|
||
<span id="cb3-154"><a href="#cb3-154" aria-hidden="true" tabindex="-1"></a> muster.append(neu)</span>
|
||
<span id="cb3-155"><a href="#cb3-155" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-156"><a href="#cb3-156" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-157"><a href="#cb3-157" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> first_fit(breiten, bedarf, rollenbreite: <span class="bu">int</span> <span class="op">=</span> ROLLENBREITE) <span class="op">-></span> <span class="bu">int</span>:</span>
|
||
<span id="cb3-158"><a href="#cb3-158" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Die Faustregel: groesstes Stueck zuerst, auf die erste passende Rolle."""</span></span>
|
||
<span id="cb3-159"><a href="#cb3-159" aria-hidden="true" tabindex="-1"></a> stuecke <span class="op">=</span> <span class="bu">sorted</span>([b <span class="cf">for</span> b, menge <span class="kw">in</span> <span class="bu">zip</span>(breiten, bedarf)</span>
|
||
<span id="cb3-160"><a href="#cb3-160" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(menge)], reverse<span class="op">=</span><span class="va">True</span>)</span>
|
||
<span id="cb3-161"><a href="#cb3-161" aria-hidden="true" tabindex="-1"></a> rollen: <span class="bu">list</span>[<span class="bu">list</span>[<span class="bu">int</span>]] <span class="op">=</span> []</span>
|
||
<span id="cb3-162"><a href="#cb3-162" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> stueck <span class="kw">in</span> stuecke:</span>
|
||
<span id="cb3-163"><a href="#cb3-163" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> rolle <span class="kw">in</span> rollen:</span>
|
||
<span id="cb3-164"><a href="#cb3-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">sum</span>(rolle) <span class="op">+</span> stueck <span class="op"><=</span> rollenbreite:</span>
|
||
<span id="cb3-165"><a href="#cb3-165" aria-hidden="true" tabindex="-1"></a> rolle.append(stueck)</span>
|
||
<span id="cb3-166"><a href="#cb3-166" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span>
|
||
<span id="cb3-167"><a href="#cb3-167" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
|
||
<span id="cb3-168"><a href="#cb3-168" aria-hidden="true" tabindex="-1"></a> rollen.append([stueck])</span>
|
||
<span id="cb3-169"><a href="#cb3-169" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">len</span>(rollen)</span>
|
||
<span id="cb3-170"><a href="#cb3-170" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-171"><a href="#cb3-171" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-172"><a href="#cb3-172" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> auswerten(name: <span class="bu">str</span>, breiten, bedarf) <span class="op">-></span> <span class="bu">dict</span>:</span>
|
||
<span id="cb3-173"><a href="#cb3-173" aria-hidden="true" tabindex="-1"></a> muster, schranke, runden <span class="op">=</span> spaltengenerierung(breiten, bedarf)</span>
|
||
<span id="cb3-174"><a href="#cb3-174" aria-hidden="true" tabindex="-1"></a> ganz, _, _ <span class="op">=</span> loese_master(muster, bedarf, ganzzahlig<span class="op">=</span><span class="va">True</span>)</span>
|
||
<span id="cb3-175"><a href="#cb3-175" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">"name"</span>: name, <span class="st">"muster_gesamt"</span>: zaehle_muster(breiten),</span>
|
||
<span id="cb3-176"><a href="#cb3-176" aria-hidden="true" tabindex="-1"></a> <span class="st">"muster_erzeugt"</span>: <span class="bu">len</span>(muster), <span class="st">"runden"</span>: runden,</span>
|
||
<span id="cb3-177"><a href="#cb3-177" aria-hidden="true" tabindex="-1"></a> <span class="st">"schranke"</span>: schranke, <span class="st">"ganzzahlig"</span>: <span class="bu">int</span>(<span class="bu">round</span>(ganz)),</span>
|
||
<span id="cb3-178"><a href="#cb3-178" aria-hidden="true" tabindex="-1"></a> <span class="st">"first_fit"</span>: first_fit(breiten, bedarf),</span>
|
||
<span id="cb3-179"><a href="#cb3-179" aria-hidden="true" tabindex="-1"></a> <span class="st">"stuecke_je_rolle"</span>: ROLLENBREITE <span class="op">/</span> np.mean(breiten)}</span>
|
||
<span id="cb3-180"><a href="#cb3-180" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-181"><a href="#cb3-181" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-182"><a href="#cb3-182" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">"__main__"</span>:</span>
|
||
<span id="cb3-183"><a href="#cb3-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-184"><a href="#cb3-184" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" SPALTENGENERIERUNG: NUR DIE MUSTER ERZEUGEN, DIE MAN BRAUCHT"</span>)</span>
|
||
<span id="cb3-185"><a href="#cb3-185" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-186"><a href="#cb3-186" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Mutterrolle </span><span class="sc">{</span>ROLLENBREITE<span class="sc">:,}</span><span class="ss"> mm, </span><span class="sc">{</span><span class="bu">len</span>(BREIT)<span class="sc">}</span><span class="ss"> bestellte Breiten, "</span></span>
|
||
<span id="cb3-187"><a href="#cb3-187" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="bu">sum</span>(BEDARF)<span class="sc">}</span><span class="ss"> Zuschnitte.</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-188"><a href="#cb3-188" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-189"><a href="#cb3-189" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 1. Zwei Formulierungen ------------------------------------------</span></span>
|
||
<span id="cb3-190"><a href="#cb3-190" aria-hidden="true" tabindex="-1"></a> stuecke <span class="op">=</span> <span class="bu">sum</span>(BEDARF)</span>
|
||
<span id="cb3-191"><a href="#cb3-191" aria-hidden="true" tabindex="-1"></a> <span class="co"># Wie viele Rollen braucht man hoechstens? Die Faustregel liefert eine</span></span>
|
||
<span id="cb3-192"><a href="#cb3-192" aria-hidden="true" tabindex="-1"></a> <span class="co"># brauchbare Obergrenze - mehr Rollen als das wird niemand benoetigen.</span></span>
|
||
<span id="cb3-193"><a href="#cb3-193" aria-hidden="true" tabindex="-1"></a> rollen_obergrenze <span class="op">=</span> first_fit(BREIT, BEDARF)</span>
|
||
<span id="cb3-194"><a href="#cb3-194" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"1. Zwei Modelle fuer dieselbe Aufgabe</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||
<span id="cb3-195"><a href="#cb3-195" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Formulierung'</span><span class="sc">:<34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Binaervariablen'</span><span class="sc">:>16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'symmetrisch?'</span><span class="sc">:>16}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-196"><a href="#cb3-196" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">70</span>)</span>
|
||
<span id="cb3-197"><a href="#cb3-197" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Stueck -> Rolle (naheliegend)'</span><span class="sc">:<34}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-198"><a href="#cb3-198" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>stuecke <span class="op">*</span> rollen_obergrenze<span class="sc">:>16,}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'ja'</span><span class="sc">:>16}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-199"><a href="#cb3-199" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'wie oft welches Muster'</span><span class="sc">:<34}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-200"><a href="#cb3-200" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'eine je Muster'</span><span class="sc">:>16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'nein'</span><span class="sc">:>16}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-201"><a href="#cb3-201" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Der naheliegende Ansatz braucht eine Variable je Stueck und Rolle:"</span>)</span>
|
||
<span id="cb3-202"><a href="#cb3-202" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>stuecke<span class="sc">}</span><span class="ss"> Zuschnitte x </span><span class="sc">{</span>rollen_obergrenze<span class="sc">}</span><span class="ss"> Rollen (Obergrenze aus der Faustregel)"</span>)</span>
|
||
<span id="cb3-203"><a href="#cb3-203" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" = </span><span class="sc">{</span>stuecke <span class="op">*</span> rollen_obergrenze<span class="sc">:,}</span><span class="ss"> Binaervariablen."</span>)</span>
|
||
<span id="cb3-204"><a href="#cb3-204" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st"> Schlimmer als die Zahl ist die Symmetrie: Alle Mutterrollen sind"</span>)</span>
|
||
<span id="cb3-205"><a href="#cb3-205" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" gleich, also beschreibt jede Loesung dieselbe Schnittvorschrift in"</span>)</span>
|
||
<span id="cb3-206"><a href="#cb3-206" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" unzaehligen Umbenennungen. Branch-and-Bound probiert sie einzeln"</span>)</span>
|
||
<span id="cb3-207"><a href="#cb3-207" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" durch und kommt nicht voran - der Suchbaum waechst, ohne dass sich"</span>)</span>
|
||
<span id="cb3-208"><a href="#cb3-208" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" die Schranke bewegt."</span>)</span>
|
||
<span id="cb3-209"><a href="#cb3-209" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st"> Das Mustermodell hat je Muster genau eine Variable und kennt keine"</span>)</span>
|
||
<span id="cb3-210"><a href="#cb3-210" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einzelnen Rollen mehr. Damit ist die Symmetrie weg. Sein Problem ist"</span>)</span>
|
||
<span id="cb3-211"><a href="#cb3-211" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ein anderes - und zwar das folgende."</span>)</span>
|
||
<span id="cb3-212"><a href="#cb3-212" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-213"><a href="#cb3-213" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 2. Die Musterzahl ------------------------------------------------</span></span>
|
||
<span id="cb3-214"><a href="#cb3-214" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-215"><a href="#cb3-215" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"2. Warum man die Muster nicht aufschreiben kann</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||
<span id="cb3-216"><a href="#cb3-216" aria-hidden="true" tabindex="-1"></a> gross <span class="op">=</span> auswerten(<span class="st">"breite Zuschnitte (1.380-2.200 mm)"</span>, BREIT, BEDARF)</span>
|
||
<span id="cb3-217"><a href="#cb3-217" aria-hidden="true" tabindex="-1"></a> schmal <span class="op">=</span> auswerten(<span class="st">"schmale Zuschnitte (380-1.200 mm)"</span>, SCHMAL, BEDARF)</span>
|
||
<span id="cb3-218"><a href="#cb3-218" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-219"><a href="#cb3-219" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Instanz'</span><span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Muster'</span><span class="sc">:>12}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'davon erzeugt'</span><span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Anteil'</span><span class="sc">:>9}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-220"><a href="#cb3-220" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">76</span>)</span>
|
||
<span id="cb3-221"><a href="#cb3-221" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> fall <span class="kw">in</span> (gross, schmal):</span>
|
||
<span id="cb3-222"><a href="#cb3-222" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> fall[<span class="st">"muster_erzeugt"</span>] <span class="op">/</span> fall[<span class="st">"muster_gesamt"</span>]</span>
|
||
<span id="cb3-223"><a href="#cb3-223" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>fall[<span class="st">'name'</span>]<span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span>fall[<span class="st">'muster_gesamt'</span>]<span class="sc">:>12,}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-224"><a href="#cb3-224" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>fall[<span class="st">'muster_erzeugt'</span>]<span class="sc">:>14}</span><span class="ss"> </span><span class="sc">{</span>anteil<span class="sc">:>8.1%}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-225"><a href="#cb3-225" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-226"><a href="#cb3-226" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Schon das Halbieren der Breiten laesst die Musterzahl von "</span></span>
|
||
<span id="cb3-227"><a href="#cb3-227" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>gross[<span class="st">'muster_gesamt'</span>]<span class="sc">:,}</span><span class="ss"> auf"</span>)</span>
|
||
<span id="cb3-228"><a href="#cb3-228" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>schmal[<span class="st">'muster_gesamt'</span>]<span class="sc">:,}</span><span class="ss"> springen - Faktor "</span></span>
|
||
<span id="cb3-229"><a href="#cb3-229" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>schmal[<span class="st">'muster_gesamt'</span>] <span class="op">/</span> gross[<span class="st">'muster_gesamt'</span>]<span class="sc">:.0f}</span><span class="ss">. Bei einer echten "</span></span>
|
||
<span id="cb3-230"><a href="#cb3-230" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Papierfabrik mit"</span>)</span>
|
||
<span id="cb3-231"><a href="#cb3-231" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" vierzig Breiten und Millimeterschritten sind es mehr, als sich"</span>)</span>
|
||
<span id="cb3-232"><a href="#cb3-232" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" speichern liesse."</span>)</span>
|
||
<span id="cb3-233"><a href="#cb3-233" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Gebraucht werden davon </span><span class="sc">{</span>schmal[<span class="st">'muster_erzeugt'</span>]<span class="sc">}</span><span class="ss"> - "</span></span>
|
||
<span id="cb3-234"><a href="#cb3-234" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>schmal[<span class="st">'muster_erzeugt'</span>] <span class="op">/</span> schmal[<span class="st">'muster_gesamt'</span>]<span class="sc">:.2%}</span><span class="ss"> der Gesamtzahl."</span>)</span>
|
||
<span id="cb3-235"><a href="#cb3-235" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-236"><a href="#cb3-236" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 3. Die Schleife --------------------------------------------------</span></span>
|
||
<span id="cb3-237"><a href="#cb3-237" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-238"><a href="#cb3-238" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"3. Die Schleife an der breiten Instanz</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||
<span id="cb3-239"><a href="#cb3-239" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Startbasis: </span><span class="sc">{</span><span class="bu">len</span>(BREIT)<span class="sc">}</span><span class="ss"> triviale Muster (je Rolle nur eine Breite)"</span>)</span>
|
||
<span id="cb3-240"><a href="#cb3-240" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Runden bis kein Muster mehr lohnt: </span><span class="sc">{</span>gross[<span class="st">'runden'</span>]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-241"><a href="#cb3-241" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Muster am Ende: </span><span class="sc">{</span>gross[<span class="st">'muster_erzeugt'</span>]<span class="sc">}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-242"><a href="#cb3-242" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" LP-Schranke: </span><span class="sc">{</span>gross[<span class="st">'schranke'</span>]<span class="sc">:.4f}</span><span class="ss"> Rollen"</span>)</span>
|
||
<span id="cb3-243"><a href="#cb3-243" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Die LP-Schranke ist eine ZUSAGE: Weniger als "</span></span>
|
||
<span id="cb3-244"><a href="#cb3-244" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>np<span class="sc">.</span>ceil(gross[<span class="st">'schranke'</span>] <span class="op">-</span> <span class="fl">1e-9</span>)<span class="sc">:.0f}</span><span class="ss"> Rollen sind"</span>)</span>
|
||
<span id="cb3-245"><a href="#cb3-245" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" nicht moeglich - unabhaengig davon, wie clever man weiterschneidet."</span>)</span>
|
||
<span id="cb3-246"><a href="#cb3-246" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Genau diese Aussage fehlt einer Heuristik (Kapitel Metaheuristiken)."</span>)</span>
|
||
<span id="cb3-247"><a href="#cb3-247" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-248"><a href="#cb3-248" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 4. Wann es sich lohnt --------------------------------------------</span></span>
|
||
<span id="cb3-249"><a href="#cb3-249" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-250"><a href="#cb3-250" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"4. Wann sich der Aufwand lohnt - und wann nicht</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
||
<span id="cb3-251"><a href="#cb3-251" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Instanz'</span><span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Faustregel'</span><span class="sc">:>11}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'exakt'</span><span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Schranke'</span><span class="sc">:>10}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-252"><a href="#cb3-252" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Ersparnis'</span><span class="sc">:>11}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-253"><a href="#cb3-253" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">80</span>)</span>
|
||
<span id="cb3-254"><a href="#cb3-254" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> fall <span class="kw">in</span> (gross, schmal):</span>
|
||
<span id="cb3-255"><a href="#cb3-255" aria-hidden="true" tabindex="-1"></a> ersparnis <span class="op">=</span> (fall[<span class="st">"first_fit"</span>] <span class="op">-</span> fall[<span class="st">"ganzzahlig"</span>]) <span class="op">/</span> fall[<span class="st">"first_fit"</span>]</span>
|
||
<span id="cb3-256"><a href="#cb3-256" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>fall[<span class="st">'name'</span>]<span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span>fall[<span class="st">'first_fit'</span>]<span class="sc">:>11}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-257"><a href="#cb3-257" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>fall[<span class="st">'ganzzahlig'</span>]<span class="sc">:>8}</span><span class="ss"> </span><span class="sc">{</span>fall[<span class="st">'schranke'</span>]<span class="sc">:>10.2f}</span><span class="ss"> "</span></span>
|
||
<span id="cb3-258"><a href="#cb3-258" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>ersparnis<span class="sc">:>10.0%}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-259"><a href="#cb3-259" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-260"><a href="#cb3-260" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Bei den breiten Zuschnitten spart die Spaltengenerierung "</span></span>
|
||
<span id="cb3-261"><a href="#cb3-261" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>gross[<span class="st">'first_fit'</span>] <span class="op">-</span> gross[<span class="st">'ganzzahlig'</span>]<span class="sc">}</span><span class="ss"> von"</span>)</span>
|
||
<span id="cb3-262"><a href="#cb3-262" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>gross[<span class="st">'first_fit'</span>]<span class="sc">}</span><span class="ss"> Rollen. Bei den schmalen spart sie NICHTS - dort ist die"</span>)</span>
|
||
<span id="cb3-263"><a href="#cb3-263" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Faustregel bereits optimal."</span>)</span>
|
||
<span id="cb3-264"><a href="#cb3-264" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Der Unterschied haengt an einer einzigen Kennzahl:</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-265"><a href="#cb3-265" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Instanz'</span><span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Stuecke je Rolle (etwa)'</span><span class="sc">:>24}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-266"><a href="#cb3-266" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">62</span>)</span>
|
||
<span id="cb3-267"><a href="#cb3-267" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> fall <span class="kw">in</span> (gross, schmal):</span>
|
||
<span id="cb3-268"><a href="#cb3-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>fall[<span class="st">'name'</span>]<span class="sc">:<36}</span><span class="ss"> </span><span class="sc">{</span>fall[<span class="st">'stuecke_je_rolle'</span>]<span class="sc">:>24.1f}</span><span class="ss">"</span>)</span>
|
||
<span id="cb3-269"><a href="#cb3-269" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-270"><a href="#cb3-270" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st"> Passen nur zwei bis drei Stuecke auf eine Rolle, entscheidet jede"</span>)</span>
|
||
<span id="cb3-271"><a href="#cb3-271" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" einzelne Zuordnung viel, und eine kurzsichtige Regel verschenkt"</span>)</span>
|
||
<span id="cb3-272"><a href="#cb3-272" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ganze Rollen. Passen sechs oder mehr darauf, gleichen sich die"</span>)</span>
|
||
<span id="cb3-273"><a href="#cb3-273" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Fehler aus - die Reste sind klein gegen die Rollenbreite, und die"</span>)</span>
|
||
<span id="cb3-274"><a href="#cb3-274" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Faustregel trifft es fast immer."</span>)</span>
|
||
<span id="cb3-275"><a href="#cb3-275" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb3-276"><a href="#cb3-276" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-277"><a href="#cb3-277" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN DARAUS MITNIMMT"</span>)</span>
|
||
<span id="cb3-278"><a href="#cb3-278" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">84</span>)</span>
|
||
<span id="cb3-279"><a href="#cb3-279" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"1. Der Perspektivwechsel ist die eigentliche Arbeit: nicht 'welches"</span>)</span>
|
||
<span id="cb3-280"><a href="#cb3-280" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Stueck wohin', sondern 'wie oft welches Muster'. Damit verschwindet"</span>)</span>
|
||
<span id="cb3-281"><a href="#cb3-281" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" die Symmetrie, die das naheliegende Modell unloesbar macht."</span>)</span>
|
||
<span id="cb3-282"><a href="#cb3-282" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"2. Der Preis dafuer ist eine unaufschreibbare Zahl von Variablen. Die"</span>)</span>
|
||
<span id="cb3-283"><a href="#cb3-283" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Spaltengenerierung zahlt ihn nicht, sondern erzeugt nur die wenigen"</span>)</span>
|
||
<span id="cb3-284"><a href="#cb3-284" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Spalten, die die Dualwerte als lohnend ausweisen."</span>)</span>
|
||
<span id="cb3-285"><a href="#cb3-285" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"3. Das Teilproblem ist ein Rucksack - ein Verfahren, das die Leser"</span>)</span>
|
||
<span id="cb3-286"><a href="#cb3-286" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" dieses Buchs schon kennen. Neu ist allein die Schleife."</span>)</span>
|
||
<span id="cb3-287"><a href="#cb3-287" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"4. Und die unbequeme Erkenntnis: Ob sich das alles lohnt, entscheidet"</span>)</span>
|
||
<span id="cb3-288"><a href="#cb3-288" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" die Instanz, nicht die Methode. Bei sechs Stuecken je Rolle ist die"</span>)</span>
|
||
<span id="cb3-289"><a href="#cb3-289" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Faustregel so gut wie das Optimum - und der Ertrag der"</span>)</span>
|
||
<span id="cb3-290"><a href="#cb3-290" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Spaltengenerierung liegt dann allein in der SCHRANKE, die beweist,"</span>)</span>
|
||
<span id="cb3-291"><a href="#cb3-291" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" dass man aufhoeren kann zu suchen."</span>)</span>
|
||
<span id="cb3-292"><a href="#cb3-292" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">84</span>)</span></code></pre></div>
|
||
<p><strong>Erwartete Ausgabe:</strong></p>
|
||
<pre><code>====================================================================================
|
||
SPALTENGENERIERUNG: NUR DIE MUSTER ERZEUGEN, DIE MAN BRAUCHT
|
||
====================================================================================
|
||
Mutterrolle 5,600 mm, 13 bestellte Breiten, 219 Zuschnitte.
|
||
|
||
1. Zwei Modelle fuer dieselbe Aufgabe
|
||
|
||
Formulierung Binaervariablen symmetrisch?
|
||
----------------------------------------------------------------------
|
||
Stueck -> Rolle (naheliegend) 17,958 ja
|
||
wie oft welches Muster eine je Muster nein
|
||
|
||
Der naheliegende Ansatz braucht eine Variable je Stueck und Rolle:
|
||
219 Zuschnitte x 82 Rollen (Obergrenze aus der Faustregel)
|
||
= 17,958 Binaervariablen.
|
||
|
||
Schlimmer als die Zahl ist die Symmetrie: Alle Mutterrollen sind
|
||
gleich, also beschreibt jede Loesung dieselbe Schnittvorschrift in
|
||
unzaehligen Umbenennungen. Branch-and-Bound probiert sie einzeln
|
||
durch und kommt nicht voran - der Suchbaum waechst, ohne dass sich
|
||
die Schranke bewegt.
|
||
|
||
Das Mustermodell hat je Muster genau eine Variable und kennt keine
|
||
einzelnen Rollen mehr. Damit ist die Symmetrie weg. Sein Problem ist
|
||
ein anderes - und zwar das folgende.
|
||
|
||
------------------------------------------------------------------------------------
|
||
2. Warum man die Muster nicht aufschreiben kann
|
||
|
||
Instanz Muster davon erzeugt Anteil
|
||
----------------------------------------------------------------------------
|
||
breite Zuschnitte (1.380-2.200 mm) 309 38 12.3%
|
||
schmale Zuschnitte (380-1.200 mm) 46,408 28 0.1%
|
||
|
||
Schon das Halbieren der Breiten laesst die Musterzahl von 309 auf
|
||
46,408 springen - Faktor 150. Bei einer echten Papierfabrik mit
|
||
vierzig Breiten und Millimeterschritten sind es mehr, als sich
|
||
speichern liesse.
|
||
|
||
Gebraucht werden davon 28 - 0.06% der Gesamtzahl.
|
||
|
||
------------------------------------------------------------------------------------
|
||
3. Die Schleife an der breiten Instanz
|
||
|
||
Startbasis: 13 triviale Muster (je Rolle nur eine Breite)
|
||
Runden bis kein Muster mehr lohnt: 26
|
||
Muster am Ende: 38
|
||
LP-Schranke: 72.9167 Rollen
|
||
|
||
Die LP-Schranke ist eine ZUSAGE: Weniger als 73 Rollen sind
|
||
nicht moeglich - unabhaengig davon, wie clever man weiterschneidet.
|
||
Genau diese Aussage fehlt einer Heuristik (Kapitel Metaheuristiken).
|
||
|
||
------------------------------------------------------------------------------------
|
||
4. Wann sich der Aufwand lohnt - und wann nicht
|
||
|
||
Instanz Faustregel exakt Schranke Ersparnis
|
||
--------------------------------------------------------------------------------
|
||
breite Zuschnitte (1.380-2.200 mm) 82 73 72.92 11%
|
||
schmale Zuschnitte (380-1.200 mm) 35 35 33.60 0%
|
||
|
||
Bei den breiten Zuschnitten spart die Spaltengenerierung 9 von
|
||
82 Rollen. Bei den schmalen spart sie NICHTS - dort ist die
|
||
Faustregel bereits optimal.
|
||
|
||
Der Unterschied haengt an einer einzigen Kennzahl:
|
||
|
||
Instanz Stuecke je Rolle (etwa)
|
||
--------------------------------------------------------------
|
||
breite Zuschnitte (1.380-2.200 mm) 3.0
|
||
schmale Zuschnitte (380-1.200 mm) 6.4
|
||
|
||
Passen nur zwei bis drei Stuecke auf eine Rolle, entscheidet jede
|
||
einzelne Zuordnung viel, und eine kurzsichtige Regel verschenkt
|
||
ganze Rollen. Passen sechs oder mehr darauf, gleichen sich die
|
||
Fehler aus - die Reste sind klein gegen die Rollenbreite, und die
|
||
Faustregel trifft es fast immer.
|
||
|
||
====================================================================================
|
||
WAS MAN DARAUS MITNIMMT
|
||
====================================================================================
|
||
1. Der Perspektivwechsel ist die eigentliche Arbeit: nicht 'welches
|
||
Stueck wohin', sondern 'wie oft welches Muster'. Damit verschwindet
|
||
die Symmetrie, die das naheliegende Modell unloesbar macht.
|
||
2. Der Preis dafuer ist eine unaufschreibbare Zahl von Variablen. Die
|
||
Spaltengenerierung zahlt ihn nicht, sondern erzeugt nur die wenigen
|
||
Spalten, die die Dualwerte als lohnend ausweisen.
|
||
3. Das Teilproblem ist ein Rucksack - ein Verfahren, das die Leser
|
||
dieses Buchs schon kennen. Neu ist allein die Schleife.
|
||
4. Und die unbequeme Erkenntnis: Ob sich das alles lohnt, entscheidet
|
||
die Instanz, nicht die Methode. Bei sechs Stuecken je Rolle ist die
|
||
Faustregel so gut wie das Optimum - und der Ertrag der
|
||
Spaltengenerierung liegt dann allein in der SCHRANKE, die beweist,
|
||
dass man aufhoeren kann zu suchen.
|
||
====================================================================================</code></pre>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-befund">10.6 Was die Zahlen zeigen</h2>
|
||
<h3 id="die-musterzahl-explodiert-der-bedarf-nicht">Die Musterzahl explodiert, der Bedarf nicht</h3>
|
||
<table>
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Instanz</th>
|
||
<th style="text-align: right;">mögliche Muster</th>
|
||
<th style="text-align: right;">erzeugt</th>
|
||
<th style="text-align: right;">Anteil</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>breite Zuschnitte (1 380–2 200 mm)</td>
|
||
<td style="text-align: right;">309</td>
|
||
<td style="text-align: right;">38</td>
|
||
<td style="text-align: right;">12,3 %</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>schmale Zuschnitte (380–1 200 mm)</td>
|
||
<td style="text-align: right;">46 408</td>
|
||
<td style="text-align: right;">28</td>
|
||
<td style="text-align: right;"><strong>0,06 %</strong></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<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>
|
||
<h3 id="der-unbequeme-teil-es-lohnt-sich-nicht-immer">Der unbequeme Teil: Es lohnt sich nicht immer</h3>
|
||
<table>
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Instanz</th>
|
||
<th style="text-align: right;">Faustregel</th>
|
||
<th style="text-align: right;">exakt</th>
|
||
<th style="text-align: right;">Schranke</th>
|
||
<th style="text-align: right;">Ersparnis</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>breite Zuschnitte</td>
|
||
<td style="text-align: right;">82 Rollen</td>
|
||
<td style="text-align: right;"><strong>73</strong></td>
|
||
<td style="text-align: right;">72,92</td>
|
||
<td style="text-align: right;"><strong>11 %</strong></td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>schmale Zuschnitte</td>
|
||
<td style="text-align: right;">35 Rollen</td>
|
||
<td style="text-align: right;">35</td>
|
||
<td style="text-align: right;">33,60</td>
|
||
<td style="text-align: right;"><strong>0 %</strong></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<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>
|
||
<tr class="header">
|
||
<th>Instanz</th>
|
||
<th style="text-align: right;">Stücke je Rolle</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>breite Zuschnitte</td>
|
||
<td style="text-align: right;">3,0</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>schmale Zuschnitte</td>
|
||
<td style="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 <a href="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>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-uebungsaufgaben">10.7 Übungsaufgaben</h2>
|
||
<blockquote>
|
||
<p>Lösungen: <a href="anhang-loesungen.html#sec:loesungen-dekomposition">Abschnitt A.10</a>.</p>
|
||
</blockquote>
|
||
<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 <a href="#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 />
|
||
<h2 id="sec:dekomposition-denkfehler">10.8 Finde den Denkfehler</h2>
|
||
<div class="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>
|
||
</blockquote>
|
||
</div>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-quiz">10.9 Micro-Quiz</h2>
|
||
<div class="card card-quiz">
|
||
<blockquote>
|
||
<p><strong>❓ Drei Fragen</strong></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>
|
||
</blockquote>
|
||
</div>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-selbsttest">10.10 Selbsttest</h2>
|
||
<ol type="1">
|
||
<li>Erklären Sie Symmetrie in einem MILP an einem Beispiel aus Ihrem Arbeitsumfeld.</li>
|
||
<li>Was ist beim Zuschnitt eine „Spalte”, und was steht darin?</li>
|
||
<li>Woher kommen die Preise, mit denen das Teilproblem rechnet?</li>
|
||
<li>Warum ist das Verfahren exakt, obwohl es fast alle Muster nie ansieht?</li>
|
||
<li>Sie sollen vor dem Projekt abschätzen, ob sich Spaltengenerierung lohnt. Welche eine Zahl rechnen Sie aus?</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="sec:dekomposition-zusammenfassung">10.11 Zusammenfassung</h2>
|
||
<ul>
|
||
<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>
|
||
</ul>
|
||
|
||
</article>
|
||
<button type="button" class="fortschritt-knopf" data-kapitel="dekomposition.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
|
||
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="metaheuristiken.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a><a class="prev-next-knopf prev-next-next" href="synthese-kernverfahren.html"><span><small>Weiter</small>Synthese Teil II</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
|
||
</main>
|
||
</div>
|
||
<footer class="site-footer">
|
||
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
|
||
<a href="programme.html">Beispielprogramme</a></p>
|
||
</footer>
|
||
<script defer src="assets/search-index.js"></script>
|
||
<script defer src="assets/programme.js"></script>
|
||
<script defer src="assets/site.js"></script>
|
||
</body>
|
||
</html>
|