Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
b7af2f1d9a
468 changed files with 262141 additions and 0 deletions
78
OR_HTML_04/programme/Dualitaet_Nachweis.py
Normal file
78
OR_HTML_04/programme/Dualitaet_Nachweis.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Dualitaet_Nachweis.py
|
||||
"""
|
||||
Kapitel LP: Primales und duales Problem unabhaengig loesen und den starken
|
||||
Dualitaetssatz sowie den komplementaeren Schlupf numerisch nachweisen.
|
||||
|
||||
Modell aus der Simplex-Handrechnung (Bot-Allokation, LP-Relaxation):
|
||||
max 150*x1 + 250*x2 u.d.N. 2*x1+5*x2<=40, 4*x1+6*x2<=60, x1<=8, x>=0
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy.optimize import linprog
|
||||
|
||||
# --- Primales Problem --------------------------------------------------------
|
||||
C_PRIMAL = np.array([150.0, 250.0])
|
||||
A_PRIMAL = np.array([[2.0, 5.0], [4.0, 6.0], [1.0, 0.0]])
|
||||
B_PRIMAL = np.array([40.0, 60.0, 8.0])
|
||||
|
||||
# --- Duales Problem: min b^T y u.d.N. A^T y >= c, y >= 0 ------------------
|
||||
# linprog kennt nur <=, also A^T y >= c <=> -A^T y <= -c
|
||||
C_DUAL = B_PRIMAL
|
||||
A_DUAL = -A_PRIMAL.T
|
||||
B_DUAL = -C_PRIMAL
|
||||
|
||||
|
||||
def loese():
|
||||
primal = linprog(c=-C_PRIMAL, A_ub=A_PRIMAL, b_ub=B_PRIMAL,
|
||||
bounds=[(0, None)] * 2, method="highs")
|
||||
dual = linprog(c=C_DUAL, A_ub=A_DUAL, b_ub=B_DUAL,
|
||||
bounds=[(0, None)] * 3, method="highs")
|
||||
if not primal.success or not dual.success:
|
||||
raise SystemExit("Primal oder Dual nicht loesbar.")
|
||||
return primal, dual
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
primal, dual = loese()
|
||||
x = primal.x
|
||||
y = dual.x
|
||||
z_primal = -primal.fun
|
||||
z_dual = dual.fun
|
||||
|
||||
print("=" * 78)
|
||||
print(" PRIMALES PROBLEM")
|
||||
print("=" * 78)
|
||||
print(f"x* = ({x[0]:.4f}, {x[1]:.4f})")
|
||||
print(f"Z* = {z_primal:.4f}")
|
||||
|
||||
print("\n" + "=" * 78)
|
||||
print(" DUALES PROBLEM")
|
||||
print("=" * 78)
|
||||
print(f"y* = ({y[0]:.4f}, {y[1]:.4f}, {y[2]:.4f})")
|
||||
print(f"W* = {z_dual:.4f}")
|
||||
|
||||
print("\n" + "=" * 78)
|
||||
print(" STARKER DUALITAETSSATZ: c^T x* == b^T y* ?")
|
||||
print("=" * 78)
|
||||
print(f" Primal Z* = {z_primal:.6f}")
|
||||
print(f" Dual W* = {z_dual:.6f}")
|
||||
differenz = abs(z_primal - z_dual)
|
||||
print(f" Differenz = {differenz:.2e} -> "
|
||||
f"{'BESTAETIGT' if differenz < 1e-6 else 'VERLETZT!'}")
|
||||
|
||||
print("\n" + "=" * 78)
|
||||
print(" KOMPLEMENTAERER SCHLUPF: s_i * y_i == 0 fuer alle i ?")
|
||||
print("=" * 78)
|
||||
schlupf = B_PRIMAL - A_PRIMAL @ x
|
||||
ressourcen = ["vCPU (s1)", "RAM (s2)", "Marktlimit (s3)"]
|
||||
for name, s, yi in zip(ressourcen, schlupf, y):
|
||||
produkt = s * yi
|
||||
print(f" {name:<16} Schlupf s={s:6.4f} Schattenpreis y={yi:6.4f} "
|
||||
f"s*y={produkt:.2e} {'OK' if abs(produkt) < 1e-6 else 'VERLETZT!'}")
|
||||
|
||||
print("\nFazit: Das dual geloeste y* stimmt exakt mit den Schattenpreisen")
|
||||
print("überein, die die Simplex-Rechnung von Hand in der Z-Zeile")
|
||||
print("ablas - unabhaengig voneinander berechnet, identisches Ergebnis.")
|
||||
print("=" * 78)
|
||||
Loading…
Reference in a new issue