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
85
OR_HTML_04/programme/Bellman_Minimalbeispiel.py
Normal file
85
OR_HTML_04/programme/Bellman_Minimalbeispiel.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Bellman_Minimalbeispiel.py
|
||||
"""
|
||||
Kapitel Dynamische Programmierung: Die Handrechnung zur Rueckwaertsinduktion als Code.
|
||||
Zeigt die Wertfunktionstabelle und die optimale Politik Schritt fuer Schritt.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
GESAMT = 3 # zu verkaufende Einheiten
|
||||
PERIODEN = 2 # Anzahl Verkaufsperioden
|
||||
|
||||
|
||||
def kosten(menge: int) -> float:
|
||||
"""Ueberproportionale Marktauswirkung: doppelte Menge kostet vierfach."""
|
||||
return float(menge ** 2)
|
||||
|
||||
|
||||
def loese_rueckwaerts():
|
||||
# V[t, x] = minimale Restkosten, wenn zu Beginn von Periode t noch x Stueck offen sind
|
||||
V = np.full((PERIODEN + 1, GESAMT + 1), np.inf)
|
||||
politik = np.zeros((PERIODEN, GESAMT + 1), dtype=int)
|
||||
|
||||
# Endbedingung: nach der letzten Periode darf nichts mehr offen sein
|
||||
V[PERIODEN, 0] = 0.0
|
||||
|
||||
print("=" * 70)
|
||||
print(" RUECKWAERTSINDUKTION SCHRITT FUER SCHRITT")
|
||||
print("=" * 70)
|
||||
|
||||
for t in range(PERIODEN - 1, -1, -1):
|
||||
letzte_periode = (t == PERIODEN - 1)
|
||||
print(f"\nStufe t = {t}" + (" (letzte Periode: alles muss weg)" if letzte_periode
|
||||
else " (freie Wahl der Menge)"))
|
||||
print(f" {'Zustand x':>10} | {'beste Aktion':>12} | {'Sofortkosten':>13} | "
|
||||
f"{'V[t+1]':>9} | {'V[t]':>8}")
|
||||
print(" " + "-" * 62)
|
||||
|
||||
for x in range(GESAMT + 1):
|
||||
aktionen = [x] if letzte_periode else range(x + 1)
|
||||
bester_wert, beste_aktion, beste_teile = np.inf, 0, (0.0, 0.0)
|
||||
|
||||
for n in aktionen:
|
||||
rest = x - n
|
||||
sofort = kosten(n)
|
||||
zukunft = V[t + 1, rest]
|
||||
gesamt = sofort + zukunft
|
||||
if gesamt < bester_wert:
|
||||
bester_wert, beste_aktion = gesamt, n
|
||||
beste_teile = (sofort, zukunft)
|
||||
|
||||
V[t, x] = bester_wert
|
||||
politik[t, x] = beste_aktion
|
||||
print(f" {x:>10} | {beste_aktion:>12} | {beste_teile[0]:>13.1f} | "
|
||||
f"{beste_teile[1]:>9.1f} | {bester_wert:>8.1f}")
|
||||
|
||||
return V, politik
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
V, politik = loese_rueckwaerts()
|
||||
|
||||
# --- Vorwaertspfad: der optimalen Politik folgen ---------------------
|
||||
print("\n" + "=" * 70)
|
||||
print(" OPTIMALER PFAD (Vorwaertssimulation)")
|
||||
print("=" * 70)
|
||||
bestand = GESAMT
|
||||
gesamtkosten = 0.0
|
||||
for t in range(PERIODEN):
|
||||
aktion = politik[t, bestand]
|
||||
gesamtkosten += kosten(aktion)
|
||||
print(f" Periode {t}: Bestand {bestand} -> verkaufe {aktion} "
|
||||
f"(Kosten {kosten(aktion):.1f}) -> Rest {bestand - aktion}")
|
||||
bestand -= aktion
|
||||
|
||||
print(f"\n Gesamtkosten: {gesamtkosten:.1f} (V[0, {GESAMT}] = {V[0, GESAMT]:.1f})")
|
||||
assert abs(gesamtkosten - V[0, GESAMT]) < 1e-9, "Pfadkosten != Wertfunktion!"
|
||||
|
||||
# --- Vergleich mit naiven Strategien ---------------------------------
|
||||
alles_sofort = kosten(GESAMT)
|
||||
print(f"\n Zum Vergleich - alles in Periode 0 verkaufen: {alles_sofort:.1f}")
|
||||
print(f" Ersparnis durch Stueckelung: {alles_sofort - gesamtkosten:.1f} "
|
||||
f"({(1 - gesamtkosten/alles_sofort)*100:.0f} %)")
|
||||
print("=" * 70)
|
||||
Loading…
Reference in a new issue