Kapitel 4: Vom Management-Wunsch zum Modell

📌 Kapitel auf einen Blick

Worum geht es? Um den Schritt, der vor jedem Solver kommt und über den kaum ein Lehrbuch spricht: aus einem Satz in einer Besprechung ein Modell zu machen. Genau hier scheitern Projekte — nicht an der Mathematik.

Voraussetzungen: Kapitel 1 (die vier Bausteine eines Modells). Kein Solverwissen nötig; das ganze Kapitel kommt mit einer einzigen linprog-Zeile aus.

Danach können Sie: eine Anforderung daraufhin abklopfen, was sie nicht sagt; die Zielgröße so wählen, dass sie keinen Fehlanreiz setzt; harte von weichen Regeln unterscheiden — und begründen, warum diese Unterscheidung ein Jahr später über die Betriebsfähigkeit entscheidet.

Zeitbedarf: ca. 3 Stunden.

Programme:
Vom_Wunsch_zum_Modell.py

Notebook: modellierung.ipynb
In Google Colab öffnen


4.1 In 5 Minuten gelöst

🚀 In 5 Minuten gelöst: „Maximieren Sie den Umsatz”

Eine Lohnfertigung hat fünf Anfragen und 60 freie Maschinenstunden. Die Anfragen verlangen zusammen 160 Stunden — es muss ausgewählt werden.

Auftrag Stunden Umsatz Material Deckungsbeitrag
A 60 60 000 € 55 000 € 5 000 €
B 30 22 000 € 12 000 € 10 000 €
C 30 20 000 € 11 000 € 9 000 €
D 25 18 000 € 9 000 € 9 000 €
E 15 12 000 € 7 000 € 5 000 €
import numpy as np
from scipy.optimize import linprog

#            Auftrag:      A       B       C       D       E
stunden  = np.array([     60,     30,     30,     25,     15], dtype=float)
umsatz   = np.array([ 60_000, 22_000, 20_000, 18_000, 12_000], dtype=float)
material = np.array([ 55_000, 12_000, 11_000,  9_000,  7_000], dtype=float)
KAPAZITAET = 60          # Maschinenstunden; die Anfragen verlangen 160

waehle = lambda ziel: np.round(linprog(
    -ziel, A_ub=[stunden], b_ub=[KAPAZITAET],
    bounds=(0, 1), integrality=1, method="highs").x).astype(int)

for wunsch, ziel in [("Umsatz maximieren", umsatz),
                     ("Deckungsbeitrag maximieren", umsatz - material)]:
    plan = waehle(ziel)
    print(f"{wunsch:<27} -> {''.join(b for b, ja in zip('ABCDE', plan) if ja):<5}"
          f"  Umsatz {umsatz @ plan:>7,.0f}   DB {(umsatz - material) @ plan:>6,.0f}")

Ausgabe:

Umsatz maximieren           -> A      Umsatz  60,000   DB  5,000
Deckungsbeitrag maximieren  -> BD     Umsatz  40,000   DB 19,000

Und jetzt der Punkt. Beide Modelle sind korrekt. Beide Pläne sind zulässig. Beide sind optimal — nur für verschiedene Fragen.

Wer „Umsatz maximieren” sagt, bekommt Auftrag A: 60 000 € Umsatz und 5 000 € Deckungsbeitrag. Wer „Deckungsbeitrag maximieren” sagt, bekommt B und D: 20 000 € weniger Umsatz und 14 000 € mehr Ergebnis. Der Umsatzplan verkauft die gesamte Kapazität für eine Marge von 8 %.

Niemand in der Besprechung hätte behauptet, Umsatz sei wichtiger als Ertrag. Der Satz „maximieren Sie den Umsatz” ist trotzdem gefallen, weil Umsatz die Zahl ist, die im Monatsbericht steht.

🎯 Merksatz Ein Modell rechnet nicht aus, was man will, sondern was man aufgeschrieben hat. Die Differenz zwischen beidem ist die eigentliche Arbeit dieses Kapitels.

Warum funktioniert das? Weil eine Zielfunktion eine vollständige Aussage darüber ist, was zählt. Alles, was nicht darin steht, ist dem Modell gleichgültig — nicht „weniger wichtig”, sondern wertlos. Der Umsatzplan opfert den Deckungsbeitrag nicht aus Bosheit; er sieht ihn schlicht nicht.


4.2 Lernziele

Nach diesem Kapitel können Sie …

  1. … erklären, warum die Zielgröße die folgenreichste Entscheidung eines OR-Projekts ist — folgenreicher als die Wahl des Solvers.
  2. … die vier Fragen stellen, die aus einer Anforderung ein Modell machen.
  3. … erkennen, dass ein Satz wie „Stammkunden dürfen wir nicht verlieren” mehrere Lesarten hat, und den Unterschied in Euro beziffern.
  4. … begründen, wann eine Regel hart und wann sie weich sein muss — und was eine harte Regel im Betrieb anrichtet, wenn sie einmal nicht erfüllbar ist.
  5. … einen Kennzahlen-Fehlanreiz benennen, bevor er in die Zielfunktion gerät.

4.3 Ein Satz ist noch kein Modell

Was aus einer Besprechung mitkommt, klingt meist so:

„Wir müssen die Maschinen besser auslasten. Und die Stammkunden dürfen wir nicht verlieren.”

Zwei Sätze, aus denen ein Modell werden soll. Sie enthalten: keine Entscheidungsvariable, keine messbare Zielgröße, keine Nebenbedingung. Sie enthalten nicht einmal genug, um zu erkennen, was entschieden werden soll.

Der Weg dorthin führt über vier Fragen. Sie sind unspektakulär, und genau deshalb werden sie übersprungen.

# Frage Was ohne sie passiert
1 Worüber wird entschieden? Welche Größen kann der Betrieb tatsächlich festlegen? Man modelliert Dinge, die gar nicht zur Disposition stehen — oder übersieht die eine Stellschraube, um die es geht.
2 Woran wird der Erfolg gemessen? Welche Zahl steht am Jahresende im Bericht? Man optimiert eine Kennzahl, die niemanden interessiert — oder eine, deren Maximierung schadet.
3 Was ist unverhandelbar, was nur unerwünscht? Jede unnötig harte Regel ist eine Zeitbombe: Irgendwann ist sie nicht erfüllbar, und das System antwortet gar nicht mehr.
4 Was passiert, wenn es nicht geht? Der Nachtlauf bricht ab, und niemand weiß, welche Regel schuld war.

📖 Zur zweiten Frage

Sie ist absichtlich anders gestellt als „Was ist Ihnen wichtig?“. Auf diese Frage antwortet jeder Betrieb mit einer Liste, in der alles wichtig ist. Die brauchbare Frage lautet: Welche Zahl steht am Jahresende im Bericht, und wer wird danach beurteilt?

Die Antwort ist unbequem und aufschlussreich — und sie erklärt, warum in der Besprechung „Umsatz” gesagt wird, obwohl „Deckungsbeitrag” gemeint ist.


4.4 Derselbe Datensatz, fünf Modelle

Das Programm dieses Kapitels arbeitet die vier Fragen an einer Auftragsannahme ab: 14 Anfragen verlangen 800 Maschinenstunden, verfügbar sind 300.

Es ist bewusst das einfachste Modell des ganzen Buchs — eine Binärvariable je Auftrag, eine Nebenbedingung. Alles, was sich zwischen den fünf Varianten ändert, ist die Frage.

#!/usr/bin/env python3

# Vom_Wunsch_zum_Modell.py
"""
Kapitel Modellierung: Derselbe Datensatz, fuenf zulaessige Modelle, fuenf andere Plaene.

Eine Lohnfertigung kann nicht alle Anfragen annehmen: 14 Auftraege verlangen
zusammen 800 Maschinenstunden, verfuegbar sind 300. Es muss also ausgewaehlt
werden. Die Daten stehen fest - was FEHLT, ist die Frage.

Das Programm zeigt in vier Teilen, was daran haengt:

  1. Drei naheliegende Ziele aus dem Managementgespraech ("Umsatz", "Auslastung",
     "Deckungsbeitrag") - und wie weit die Plaene auseinanderlaufen.
  2. Der Satz "Stammkunden duerfen wir nicht verlieren" in drei Lesarten. Alle
     drei sind vertretbar, alle drei kosten unterschiedlich viel.
  3. Was ein Strafgewicht daraus macht - und ab wann es dasselbe tut wie eine
     harte Regel.
  4. Was die harte Lesart ein Quartal spaeter anrichtet.

Der Punkt des Kapitels: Nicht die Daten bestimmen das Modell, sondern die
Frage. Und die steht selten in der Anforderung.

Benoetigt: numpy, scipy
"""

from __future__ import annotations

import numpy as np
from scipy.optimize import linprog

# (Nummer, Kunde, Stammkunde?, Maschinenstunden, Umsatz, Materialkosten)
AUFTRAEGE = [
    ("A-101", "Weber GmbH",     True,   40,  28_000,  19_000),
    ("A-102", "Weber GmbH",     True,   25,  15_000,   9_500),
    ("A-103", "Kranz AG",       True,   60,  33_000,  24_000),
    ("A-104", "Kranz AG",       True,   35,  19_000,  13_500),
    ("A-105", "Obermeier KG",   True,   20,  12_000,   7_000),
    ("A-106", "Siedler & Co",   False, 120,  84_000,  71_000),
    ("A-107", "Siedler & Co",   False, 110,  74_000,  63_000),
    ("A-108", "Novatek",        False,  95,  61_000,  52_000),
    ("A-109", "Novatek",        False,  30,  31_000,  17_000),
    ("A-110", "Hellwig",        False,  15,  17_000,   8_000),
    ("A-111", "Hellwig",        False,  55,  41_000,  30_000),
    ("A-112", "Pfeiffer Werke", False,  80,  46_000,  39_000),
    ("A-113", "Obermeier KG",   True,   45,  26_000,  17_000),
    ("A-114", "Pfeiffer Werke", False,  70,  52_000,  36_000),
]
KAPAZITAET = 300          # Maschinenstunden im Planungszeitraum

STUNDEN = np.array([a[3] for a in AUFTRAEGE], dtype=float)
UMSATZ = np.array([a[4] for a in AUFTRAEGE], dtype=float)
DECKUNGSBEITRAG = np.array([a[4] - a[5] for a in AUFTRAEGE], dtype=float)
IST_STAMM = np.array([a[2] for a in AUFTRAEGE])
STAMMKUNDEN = sorted({a[1] for a in AUFTRAEGE if a[2]})


def waehle_aus(ziel: np.ndarray, zusatz_ub=None, zusatz_b_ub=None,
               zusatz_eq=None, zusatz_b_eq=None, auftraege=AUFTRAEGE,
               stunden=STUNDEN, kapazitaet=KAPAZITAET):
    """Ein Auftrag wird angenommen oder nicht - mehr Modell ist das nicht.

    Der ganze Unterschied zwischen den fuenf Varianten unten steckt in 'ziel'
    und in den Zusatzbedingungen. Das Modell selbst bleibt dasselbe.
    """
    ergebnis = linprog(
        -ziel,
        A_ub=[stunden] + (zusatz_ub or []),
        b_ub=[kapazitaet] + (zusatz_b_ub or []),
        A_eq=zusatz_eq, b_eq=zusatz_b_eq,
        bounds=(0, 1), integrality=1, method="highs")
    if not ergebnis.success:
        return None
    return np.round(ergebnis.x).astype(int)


def kennzahlen(plan, auftraege=AUFTRAEGE, stunden=STUNDEN, umsatz=UMSATZ,
               db=DECKUNGSBEITRAG, ist_stamm=IST_STAMM, kapazitaet=KAPAZITAET):
    """Immer dieselben fuenf Zahlen - nur so sind die Varianten vergleichbar."""
    abgelehnte_stammauftraege = int(((1 - plan) & ist_stamm.astype(int)).sum())
    leer_ausgegangen = sum(
        1 for kunde in sorted({a[1] for a in auftraege if a[2]})
        if not any(plan[i] for i, a in enumerate(auftraege) if a[1] == kunde))
    return {"umsatz": float(umsatz @ plan), "db": float(db @ plan),
            "auslastung": float(stunden @ plan) / kapazitaet * 100,
            "stamm_abgelehnt": abgelehnte_stammauftraege,
            "kunden_leer": leer_ausgegangen, "angenommen": int(plan.sum())}


def zeile(name: str, plan) -> None:
    if plan is None:
        print(f"  {name:<34} {'UNZULAESSIG - kein Plan moeglich':>44}")
        return
    k = kennzahlen(plan)
    print(f"  {name:<34} {k['umsatz']:>10,.0f} {k['db']:>9,.0f} "
          f"{k['auslastung']:>6.0f}% {k['stamm_abgelehnt']:>7} "
          f"{k['kunden_leer']:>6} {k['angenommen']:>7}")


def kopfzeile() -> None:
    print(f"  {'':<34} {'Umsatz':>10} {'DB':>9} {'Aus-':>7} {'Stamm-':>7} "
          f"{'Kunden':>6} {'ange-':>7}")
    print(f"  {'':<34} {'':>10} {'':>9} {'lastung':>7} {'auftr.':>7} "
          f"{'ohne':>6} {'nommen':>7}")
    print("  " + "-" * 78)


if __name__ == "__main__":
    print("=" * 84)
    print("  VOM MANAGEMENT-WUNSCH ZUM MODELL")
    print("=" * 84)
    print(f"{len(AUFTRAEGE)} Anfragen verlangen {STUNDEN.sum():.0f} Maschinenstunden, "
          f"verfuegbar sind {KAPAZITAET}.")
    print(f"Es muss also ausgewaehlt werden. {len(STAMMKUNDEN)} der Kunden sind "
          f"Stammkunden:")
    print(f"  {', '.join(STAMMKUNDEN)}\n")

    # --- 1. Drei Saetze aus dem Gespraech, drei Ziele ---------------------
    print("-" * 84)
    print("1. Was im Gespraech gesagt wurde - drei naheliegende Ziele\n")
    kopfzeile()
    plaene = {}
    for name, ziel in [("\"Umsatz maximieren\"", UMSATZ),
                       ("\"Maschinen besser auslasten\"", STUNDEN),
                       ("\"Deckungsbeitrag maximieren\"", DECKUNGSBEITRAG)]:
        plaene[name] = waehle_aus(ziel)
        zeile(name, plaene[name])

    umsatzplan = kennzahlen(plaene["\"Umsatz maximieren\""])
    auslastungsplan = kennzahlen(plaene["\"Maschinen besser auslasten\""])
    dbplan = kennzahlen(plaene["\"Deckungsbeitrag maximieren\""])

    mehr_umsatz = umsatzplan["umsatz"] - dbplan["umsatz"]
    weniger_db = dbplan["db"] - umsatzplan["db"]
    print(f"\n  Der Umsatzplan bringt {mehr_umsatz:,.0f} EUR mehr Umsatz und "
          f"{weniger_db:,.0f} EUR WENIGER")
    print(f"  Deckungsbeitrag. Fuer {mehr_umsatz:,.0f} EUR Umsatz werden also "
          f"{weniger_db:,.0f} EUR Ergebnis")
    print(f"  aufgegeben - und {umsatzplan['stamm_abgelehnt']} von "
          f"{int(IST_STAMM.sum())} Stammkundenauftraegen dazu.")
    print()
    print("  \"Auslastung\" ist der haeufigste Wunsch und der schaedlichste. Der")
    print(f"  darauf optimierte Plan bringt {dbplan['db'] - auslastungsplan['db']:,.0f} EUR weniger "
          f"Deckungsbeitrag und")
    print(f"  {dbplan['umsatz'] - auslastungsplan['umsatz']:,.0f} EUR weniger Umsatz als der "
          f"DB-Plan - und erreicht dabei")
    print(f"  nicht einmal mehr Auslastung: alle drei liegen bei "
          f"{dbplan['auslastung']:.0f} %.")
    print("  Eine volle Maschine ist eben kein Ziel, sondern ein Nebenprodukt.")

    # --- 2. Ein Satz, drei Lesarten --------------------------------------
    print("\n" + "-" * 84)
    print("2. \"Stammkunden duerfen wir nicht verlieren\" - aber was heisst das?\n")
    kopfzeile()
    zeile("ohne die Regel (DB maximieren)", waehle_aus(DECKUNGSBEITRAG))

    # Lesart A: jeder Stammkundenauftrag wird angenommen
    alle_stamm = [[1.0 if IST_STAMM[i] else 0.0 for i in range(len(AUFTRAEGE))]]
    lesart_a = waehle_aus(DECKUNGSBEITRAG, zusatz_eq=alle_stamm,
                          zusatz_b_eq=[float(IST_STAMM.sum())])
    zeile("A: jeden Stammauftrag annehmen", lesart_a)

    # Lesart B: kein Stammkunde geht leer aus (mindestens ein Auftrag je Kunde)
    je_kunde_ub, je_kunde_b = [], []
    for kunde in STAMMKUNDEN:
        je_kunde_ub.append([-1.0 if a[1] == kunde else 0.0 for a in AUFTRAEGE])
        je_kunde_b.append(-1.0)
    lesart_b = waehle_aus(DECKUNGSBEITRAG, zusatz_ub=je_kunde_ub,
                          zusatz_b_ub=je_kunde_b)
    zeile("B: kein Stammkunde geht leer aus", lesart_b)

    a, b = kennzahlen(lesart_a), kennzahlen(lesart_b)
    ohne = kennzahlen(waehle_aus(DECKUNGSBEITRAG))
    print("\n  Beide Lesarten erreichen dasselbe: kein Stammkunde geht leer aus.")
    print(f"  Lesart A kostet dafuer {ohne['db'] - a['db']:,.0f} EUR Deckungsbeitrag, "
          f"Lesart B nur {ohne['db'] - b['db']:,.0f} EUR -")
    print(f"  ein Unterschied von {b['db'] - a['db']:,.0f} EUR fuer denselben Satz aus derselben")
    print("  Besprechung. Wer nicht nachfragt, entscheidet das selbst, ohne es")
    print("  zu merken.")

    # --- 3. Die weiche Variante ------------------------------------------
    print("\n" + "-" * 84)
    print("3. Dieselbe Regel als Strafkosten je abgelehntem Stammauftrag\n")
    kopfzeile()
    for strafe in (3_000, 6_000, 10_000):
        ziel = DECKUNGSBEITRAG + np.where(IST_STAMM, float(strafe), 0.0)
        zeile(f"C: Strafe {strafe:>6,} EUR je Auftrag", waehle_aus(ziel))

    print("\n  Bei 3.000 EUR aendert sich nichts: Die Strafe liegt unter dem, was")
    print("  die Auftraege an Deckungsbeitrag verdraengen wuerden. Bei 10.000 EUR")
    print("  kommt genau Lesart A heraus. Eine harte Regel ist also nichts")
    print("  anderes als eine weiche mit hinreichend grosser Strafe - was ein")
    print("  Strafgewicht sonst noch bedeutet, steht im Kapitel CP-SAT.")

    # --- 4. Was die harte Lesart ein Quartal spaeter anrichtet -----------
    print("\n" + "-" * 84)
    print("4. Ein Quartal spaeter: Weber fragt einen Grossauftrag an\n")
    erweitert = AUFTRAEGE + [("A-115", "Weber GmbH", True, 320, 210_000, 150_000)]
    stunden_neu = np.array([a[3] for a in erweitert], dtype=float)
    db_neu = np.array([a[4] - a[5] for a in erweitert], dtype=float)
    stamm_neu = np.array([a[2] for a in erweitert])

    print(f"  A-115: Weber GmbH, {erweitert[-1][3]} Maschinenstunden - allein mehr,")
    print(f"  als der ganze Zeitraum hergibt ({KAPAZITAET} h).\n")

    alle_stamm_neu = [[1.0 if stamm_neu[i] else 0.0 for i in range(len(erweitert))]]
    hart = waehle_aus(db_neu, zusatz_eq=alle_stamm_neu,
                      zusatz_b_eq=[float(stamm_neu.sum())],
                      auftraege=erweitert, stunden=stunden_neu)
    weich = waehle_aus(db_neu + np.where(stamm_neu, 10_000.0, 0.0),
                       auftraege=erweitert, stunden=stunden_neu)

    print(f"  Lesart A (hart):  "
          f"{'UNZULAESSIG - der Nachtlauf bricht ab' if hart is None else 'loesbar'}")
    if weich is not None:
        k = kennzahlen(weich, erweitert, stunden_neu,
                       np.array([a[4] for a in erweitert], dtype=float),
                       db_neu, stamm_neu)
        print(f"  Lesart C (weich): loesbar - {k['db']:,.0f} EUR Deckungsbeitrag, "
              f"{k['stamm_abgelehnt']} Stammauftrag abgelehnt")

    print("\n" + "=" * 84)
    print("  WORAUF ES ANKOMMT")
    print("=" * 84)
    print("Die Daten waren in allen vier Teilen dieselben. Was sich geaendert hat,")
    print("war nur die Frage - und mit ihr der Plan, der Deckungsbeitrag und die")
    print("Kundenbeziehung.")
    print()
    print("Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:")
    print()
    print("  1. WORAN wird der Erfolg gemessen? Nicht 'was ist Ihnen wichtig',")
    print("     sondern: Welche Zahl steht am Jahresende im Bericht? Umsatz,")
    print("     Auslastung und Deckungsbeitrag fuehren hier zu drei")
    print("     verschiedenen Plaenen.")
    print("  2. WAS GENAU heisst der Satz? 'Stammkunden nicht verlieren' hat")
    print(f"     mindestens drei Lesarten mit {b['db'] - a['db']:,.0f} EUR Unterschied. Die Frage")
    print("     'meinen Sie jeden Auftrag oder jeden Kunden?' dauert zehn")
    print("     Sekunden.")
    print("  3. WAS PASSIERT, WENN ES NICHT GEHT? Eine harte Regel, die niemand")
    print("     hinterfragt hat, macht das System eines Tages unloesbar - und")
    print("     zwar genau dann, wenn ein besonders grosser Auftrag hereinkommt.")
    print("     Weiche Regeln antworten auch dann noch.")
    print("=" * 84)

Erwartete Ausgabe:

====================================================================================
  VOM MANAGEMENT-WUNSCH ZUM MODELL
====================================================================================
14 Anfragen verlangen 800 Maschinenstunden, verfuegbar sind 300.
Es muss also ausgewaehlt werden. 3 der Kunden sind Stammkunden:
  Kranz AG, Obermeier KG, Weber GmbH

------------------------------------------------------------------------------------
1. Was im Gespraech gesagt wurde - drei naheliegende Ziele

                                         Umsatz        DB    Aus-  Stamm- Kunden   ange-
                                                          lastung  auftr.   ohne  nommen
  ------------------------------------------------------------------------------
  "Umsatz maximieren"                   227,000    66,000    100%       5      2       6
  "Maschinen besser auslasten"          205,000    54,000    100%       4      1       5
  "Deckungsbeitrag maximieren"          222,000    78,500    100%       2      1       8

  Der Umsatzplan bringt 5,000 EUR mehr Umsatz und 12,500 EUR WENIGER
  Deckungsbeitrag. Fuer 5,000 EUR Umsatz werden also 12,500 EUR Ergebnis
  aufgegeben - und 5 von 6 Stammkundenauftraegen dazu.

  "Auslastung" ist der haeufigste Wunsch und der schaedlichste. Der
  darauf optimierte Plan bringt 24,500 EUR weniger Deckungsbeitrag und
  17,000 EUR weniger Umsatz als der DB-Plan - und erreicht dabei
  nicht einmal mehr Auslastung: alle drei liegen bei 100 %.
  Eine volle Maschine ist eben kein Ziel, sondern ein Nebenprodukt.

------------------------------------------------------------------------------------
2. "Stammkunden duerfen wir nicht verlieren" - aber was heisst das?

                                         Umsatz        DB    Aus-  Stamm- Kunden   ange-
                                                          lastung  auftr.   ohne  nommen
  ------------------------------------------------------------------------------
  ohne die Regel (DB maximieren)        222,000    78,500    100%       2      1       8
  A: jeden Stammauftrag annehmen        181,000    66,000     90%       0      0       8
  B: kein Stammkunde geht leer aus      213,000    75,000     98%       2      0       8

  Beide Lesarten erreichen dasselbe: kein Stammkunde geht leer aus.
  Lesart A kostet dafuer 12,500 EUR Deckungsbeitrag, Lesart B nur 3,500 EUR -
  ein Unterschied von 9,000 EUR fuer denselben Satz aus derselben
  Besprechung. Wer nicht nachfragt, entscheidet das selbst, ohne es
  zu merken.

------------------------------------------------------------------------------------
3. Dieselbe Regel als Strafkosten je abgelehntem Stammauftrag

                                         Umsatz        DB    Aus-  Stamm- Kunden   ange-
                                                          lastung  auftr.   ohne  nommen
  ------------------------------------------------------------------------------
  C: Strafe  3,000 EUR je Auftrag       222,000    78,500    100%       2      1       8
  C: Strafe  6,000 EUR je Auftrag       205,000    73,000    100%       1      0       8
  C: Strafe 10,000 EUR je Auftrag       181,000    66,000     90%       0      0       8

  Bei 3.000 EUR aendert sich nichts: Die Strafe liegt unter dem, was
  die Auftraege an Deckungsbeitrag verdraengen wuerden. Bei 10.000 EUR
  kommt genau Lesart A heraus. Eine harte Regel ist also nichts
  anderes als eine weiche mit hinreichend grosser Strafe - was ein
  Strafgewicht sonst noch bedeutet, steht im Kapitel CP-SAT.

------------------------------------------------------------------------------------
4. Ein Quartal spaeter: Weber fragt einen Grossauftrag an

  A-115: Weber GmbH, 320 Maschinenstunden - allein mehr,
  als der ganze Zeitraum hergibt (300 h).

  Lesart A (hart):  UNZULAESSIG - der Nachtlauf bricht ab
  Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt

====================================================================================
  WORAUF ES ANKOMMT
====================================================================================
Die Daten waren in allen vier Teilen dieselben. Was sich geaendert hat,
war nur die Frage - und mit ihr der Plan, der Deckungsbeitrag und die
Kundenbeziehung.

Drei Dinge, die deshalb vor dem ersten Modell geklaert gehoeren:

  1. WORAN wird der Erfolg gemessen? Nicht 'was ist Ihnen wichtig',
     sondern: Welche Zahl steht am Jahresende im Bericht? Umsatz,
     Auslastung und Deckungsbeitrag fuehren hier zu drei
     verschiedenen Plaenen.
  2. WAS GENAU heisst der Satz? 'Stammkunden nicht verlieren' hat
     mindestens drei Lesarten mit 9,000 EUR Unterschied. Die Frage
     'meinen Sie jeden Auftrag oder jeden Kunden?' dauert zehn
     Sekunden.
  3. WAS PASSIERT, WENN ES NICHT GEHT? Eine harte Regel, die niemand
     hinterfragt hat, macht das System eines Tages unloesbar - und
     zwar genau dann, wenn ein besonders grosser Auftrag hereinkommt.
     Weiche Regeln antworten auch dann noch.
====================================================================================

Teil 1: Drei Ziele, drei Pläne

Zielgröße Umsatz Deckungsbeitrag Auslastung abgelehnte Stammaufträge
„Umsatz maximieren” 227 000 € 66 000 € 100 % 5 von 6
„Maschinen besser auslasten” 205 000 € 54 000 € 100 % 4 von 6
„Deckungsbeitrag maximieren” 222 000 € 78 500 € 100 % 2 von 6

Zwei Beobachtungen, die im Gespräch niemand vorhergesagt hätte:

Der Umsatzplan kostet 12 500 € Ergebnis, um 5 000 € Umsatz zu gewinnen — und nimmt dabei fünf von sechs Stammkundenaufträgen aus dem Plan. Das Wechselkursverhältnis ist 1 : 2,5 gegen den Betrieb.

Der Auslastungsplan ist in jeder Hinsicht der schlechteste — auch bei der Auslastung. Alle drei Pläne belegen die Maschinen zu 100 %; der eigens darauf optimierte Plan erreicht nur nicht mehr, sondern verliert dabei 24 500 € Deckungsbeitrag. Der Grund ist einfach: Bei knapper Kapazität ist eine volle Maschine keine Leistung, sondern die Voreinstellung. Zu optimieren gibt es nur, womit sie voll wird.

⚠️ Auslastung ist ein Ergebnis, kein Ziel

„Wir müssen die Maschinen besser auslasten” ist der häufigste Satz in Produktionsbesprechungen und fast immer die falsche Zielgröße. Auslastung misst, wie viel Zeit vergeht — nicht, was dabei herauskommt. Wer sie maximiert, belohnt lange, schlecht bezahlte Aufträge.

Die Ausnahme, die die Regel erklärt: Wenn Maschinen tatsächlich leer stehen, weil zu wenig hereinkommt, ist Auslastung ein sinnvolles Symptom — aber dann liegt das Problem im Vertrieb und nicht in der Planung.

Teil 2: Ein Satz, drei Lesarten

„Stammkunden dürfen wir nicht verlieren.” Jeder im Raum nickt. Niemand merkt, dass drei verschiedene Modelle gemeint sein können:

Lesart Deckungsbeitrag kostet gegenüber „ohne Regel”
ohne die Regel 78 500 €
A: jeden Stammkundenauftrag annehmen 66 000 € 12 500 €
B: kein Stammkunde geht leer aus 75 000 € 3 500 €

Beide Lesarten erreichen dasselbe Ziel — kein Stammkunde geht leer aus. Lesart A kostet dafür das Dreieinhalbfache. Der Unterschied von 9 000 € entsteht durch eine Frage, die zehn Sekunden dauert:

„Meinen Sie jeden Auftrag oder jeden Kunden?“

Wer sie nicht stellt, entscheidet sie trotzdem — nur unbewusst, beim Tippen.

Teil 3: Hart oder weich

Dieselbe Regel als Strafkosten je abgelehntem Stammkundenauftrag:

Strafe je Auftrag Deckungsbeitrag abgelehnte Stammaufträge
3 000 € 78 500 € 2
6 000 € 73 000 € 1
10 000 € 66 000 € 0

Bei 3 000 € ändert sich nichts: Die Strafe liegt unter dem Deckungsbeitrag, den die Stammkundenaufträge verdrängen würden. Bei 10 000 € kommt genau Lesart A heraus.

Daraus folgt eine Einsicht, die das Modellieren erleichtert: Eine harte Regel ist eine weiche mit hinreichend großer Strafe. Man kann also immer weich anfangen und die Strafe hochdrehen, bis die Regel greift — und sieht dabei, was sie kostet. Was ein Strafgewicht darüber hinaus bedeutet und warum zwei Gewichte einen Wechselkurs festlegen und keine Wichtigkeiten, steht in Abschnitt 7.10.

Teil 4: Ein Quartal später

Die harte Lesart A wurde eingebaut, alles lief drei Monate lang. Dann fragt Weber einen Auftrag über 320 Maschinenstunden an — mehr, als der ganze Zeitraum hergibt.

Lesart A (hart):  UNZULAESSIG - der Nachtlauf bricht ab
Lesart C (weich): loesbar - 66,000 EUR Deckungsbeitrag, 1 Stammauftrag abgelehnt

Das ist kein konstruierter Sonderfall, sondern der Normalfall: Harte Regeln scheitern nicht bei ihrer Einführung, sondern Monate später — und zwar bevorzugt dann, wenn etwas Ungewöhnliches passiert, also genau dann, wenn man die Planung am dringendsten braucht.

Die weiche Variante antwortet auch dann noch. Sie lehnt den Großauftrag ab, weist das im Bericht aus, und ein Mensch entscheidet. Das ist der ganze Unterschied — und er wird beim Schreiben der Anforderung festgelegt, nicht beim Programmieren.

🎯 Merksatz Hart ist nur, was rechtlich oder physikalisch unmöglich ist. Alles andere wird weich mit hoher Strafe. Dann liefert der Solver den am wenigsten schlechten Plan statt einer Fehlermeldung — und die Fehlermeldung ist das, was niemand gebrauchen kann.


4.5 Der Gesprächsleitfaden

Was aus den vier Teilen als Handwerkszeug bleibt — Fragen, die man in der ersten Besprechung stellt, bevor eine Zeile Code entsteht:

📎 Zwölf Fragen für das erste Gespräch

Zur Entscheidung 1. Was genau wird entschieden — und von wem heute? 2. Was steht dabei nicht zur Disposition? 3. Wie oft wird entschieden, und wie viel Zeit ist dafür?

Zum Ziel 4. Welche Zahl steht am Jahresende im Bericht? 5. Wenn genau diese Zahl maximiert würde — welcher Plan käme heraus, und wäre er akzeptabel? 6. Gibt es eine zweite Zahl, die auch stimmen muss? Was ist Ihnen ein Prozent der ersten wert, um ein Prozent der zweiten zu gewinnen?

Zu den Regeln 7. Welche dieser Regeln ist gesetzlich oder physikalisch zwingend? 8. Welche würden Sie in einer Ausnahmesituation brechen — und was wäre eine Ausnahmesituation? 9. Bei „darf nicht”: Betrifft das jeden einzelnen Fall oder das Gesamtbild?

Zum Betrieb 10. Was soll passieren, wenn es keine zulässige Lösung gibt? 11. Wer sieht das Ergebnis, und was muss er daran erkennen können? 12. Woran würden Sie in einem halben Jahr merken, dass das System nicht mehr das Richtige tut?

Frage 5 ist die wichtigste. Sie verwandelt eine abstrakte Zielangabe in einen konkreten Plan, den man ablehnen kann — und Ablehnung ist die einzige Form von Anforderungsklärung, die zuverlässig funktioniert.


4.6 Übungsaufgaben

Lösungen: Abschnitt A.4.

Aufgabe 4.1 ⭐ — Die fehlende Lesart. Der Text nennt zwei Lesarten von „Stammkunden dürfen wir nicht verlieren”. Formulieren Sie eine dritte, die weder A noch B ist, und sagen Sie, welchen Betrieb sie beschreiben würde.

Aufgabe 4.2 ⭐ — Frage 5 anwenden. Ein Krankenhaus möchte „die Wartezeiten in der Notaufnahme senken”. Wenden Sie Frage 5 an: Welcher Plan käme heraus, wenn genau die durchschnittliche Wartezeit minimiert würde — und was wäre daran nicht akzeptabel?

Aufgabe 4.3 ⭐⭐ — Der Kompromiss dazwischen. Zwischen Lesart A (12 500 €) und Lesart B (3 500 €) liegt Spielraum. Bauen Sie eine Variante, in der jeder Stammkunde mindestens die Hälfte seiner Auftragsstunden zugeteilt bekommt. Was kostet sie?

Aufgabe 4.4 ⭐⭐ — Zwei Ziele gleichzeitig. Maximieren Sie \text{DB} + \lambda \cdot \text{Umsatz} für \lambda \in \{0; 0{,}1; 0{,}3; 1\} und tragen Sie die Ergebnisse als Tabelle auf. Ab welchem \lambda kippt der Plan von der DB- in die Umsatzlösung? Was sagt dieser Wert dem Betrieb?

Aufgabe 4.5 ⭐⭐⭐ — Die Anforderung schreiben. Formulieren Sie für den Fall dieses Kapitels eine vollständige, prüfbare Anforderung: Ziel, harte Regeln, weiche Regeln mit Strafgewichten, Verhalten bei Unlösbarkeit, auszuweisende Kennzahlen. Zwei Seiten genügen — sie sind mehr wert als zwei Wochen Programmierung.


4.7 Finde den Denkfehler

🐛 „Die Engpassmaschine soll zu mindestens 92 % ausgelastet sein”

Der Werkleiter formuliert eine klare, messbare, prüfbare Anforderung:

„Die Engpassmaschine soll in jedem Planungszeitraum zu mindestens 92 % ausgelastet sein. > Das ist unsere teuerste Anlage, Leerlauf können wir uns nicht leisten.”

Der Entwickler baut sie als harte Nebenbedingung ein — sie ist ja klar formuliert. Die Abnahme läuft mit den Daten des letzten Quartals: Der Plan hält die Vorgabe ein, der Deckungsbeitrag stimmt, alle Tests grün. Das System geht in Betrieb.

Diese Anforderung enthält zwei verschiedene Fehler. Welche?

Ein Hinweis für den zweiten: Sehen Sie sich an, was das Modell im Abnahmetest tatsächlich geprüft hat — und rechnen Sie einen Monat durch, in dem weniger Anfragen hereinkommen.


4.8 Micro-Quiz

❓ Drei Fragen

1. Warum liefert „Umsatz maximieren” hier einen schlechteren Plan als „Deckungsbeitrag maximieren”, obwohl beide Modelle korrekt sind? a) Weil der Solver bei Umsatzzielen ungenauer rechnet. b) Weil alles, was nicht in der Zielfunktion steht, für das Modell wertlos ist — nicht weniger wichtig. c) Weil Umsatzdaten in der Praxis unzuverlässiger sind als Kostendaten.

2. Eine Regel als harte Nebenbedingung statt als Strafkosten zu formulieren, ist vor allem deshalb riskant, weil … a) … harte Nebenbedingungen den Solver verlangsamen. b) … das Modell unlösbar wird, sobald die Regel einmal nicht erfüllbar ist — und dann gar keine Antwort mehr liefert. c) … sich harte Regeln nachträglich nicht mehr ändern lassen.

3. Alle drei Zielgrößen erreichen 100 % Auslastung. Was folgt daraus? a) Die Auslastung war schon vorher optimal, das Projekt ist überflüssig. b) Bei knapper Kapazität ist volle Auslastung die Voreinstellung; zu entscheiden ist nur, womit die Maschine voll wird. c) Die Kapazitätsdaten sind vermutlich falsch erfasst.


4.9 Selbsttest

  1. Warum ist „Was ist Ihnen wichtig?” eine schlechte Frage und „Welche Zahl steht im Jahresbericht?” eine gute?
  2. Nennen Sie zwei Kennzahlen aus Ihrem eigenen Arbeitsumfeld, deren Maximierung schaden würde.
  3. Erklären Sie in einem Satz, warum eine harte Regel eine weiche mit großer Strafe ist — und wann der Unterschied trotzdem zählt.
  4. Ein Kunde sagt: „Kein Mitarbeiter soll mehr als zwei Wochenenden im Monat arbeiten.” Nennen Sie zwei Lesarten und je eine Situation, in der sie auseinanderfallen.
  5. Was antworten Sie auf die Frage „Was soll passieren, wenn es keine zulässige Lösung gibt?” — und warum ist „das kommt nicht vor” keine Antwort?

4.10 Zusammenfassung

  • Ein Modell rechnet aus, was aufgeschrieben wurde, nicht was gemeint war. Die Zielfunktion ist eine vollständige Aussage: Was nicht darin steht, ist wertlos.
  • Die Zielgröße ist die folgenreichste Entscheidung eines OR-Projekts. Umsatz, Auslastung und Deckungsbeitrag führten hier auf demselben Datensatz zu drei verschiedenen Plänen mit 24 500 € Unterschied.
  • Auslastung ist ein Ergebnis, kein Ziel. Bei knapper Kapazität ist die Maschine ohnehin voll; entscheidend ist, womit.
  • Ein Satz aus einer Besprechung hat meist mehrere Lesarten. „Stammkunden nicht verlieren” kostete je nach Lesart 3 500 € oder 12 500 € — bei identischem Ergebnis für die Kunden.
  • Hart ist nur, was rechtlich oder physikalisch unmöglich ist. Eine harte Regel ist eine weiche mit hinreichend großer Strafe; der Unterschied zeigt sich erst, wenn sie einmal nicht erfüllbar ist — dann liefert die harte Variante gar nichts mehr.
  • Die vier Fragen — worüber wird entschieden, woran gemessen, was ist unverhandelbar, was bei Unlösbarkeit — gehören in die erste Besprechung. Der Leitfaden in Abschnitt 4.5 macht zwölf daraus.