From f7b1267c989661ec797b527a0d7a1cca6f5d223e Mon Sep 17 00:00:00 2001 From: Jan Hoheisel Date: Sun, 1 Feb 2026 21:48:03 +0100 Subject: [PATCH] Dienstaufwand und paralleles Rechnen --- datenmodell.py | 17 +++++++++-------- elterndienstplaner.py | 31 +++++++++++++++++++------------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/datenmodell.py b/datenmodell.py index a01003e..30a7d3c 100644 --- a/datenmodell.py +++ b/datenmodell.py @@ -15,16 +15,17 @@ from csv_io import EingabeParser class Dienst: """Repräsentiert einen Diensttyp mit allen seinen Eigenschaften""" - def __init__(self, kuerzel: str, name: str, personen_anzahl: int = 1) -> None: + def __init__(self, kuerzel: str, name: str, personen_anzahl: int = 1, aufwand: int = 1) -> None: self.kuerzel: str = kuerzel self.name: str = name self.personen_anzahl: int = personen_anzahl + self.aufwand: int = aufwand def __str__(self) -> str: - return f"{self.kuerzel} ({self.name}): {self.personen_anzahl} Person(en)" + return f"{self.kuerzel} ({self.name}): {self.personen_anzahl} Person(en), Aufwand={self.aufwand}" def __repr__(self) -> str: - return f"Dienst('{self.kuerzel}', '{self.name}', {self.personen_anzahl})" + return f"Dienst('{self.kuerzel}', '{self.name}', {self.personen_anzahl}, {self.aufwand})" def braucht_mehrere_personen(self) -> bool: """Gibt True zurück, wenn mehr als eine Person benötigt wird""" @@ -49,11 +50,11 @@ class ElterndienstplanerDaten: def __init__(self) -> None: # Dienste als Liste definieren self.dienste: List[Dienst] = [ - Dienst('F', 'Frühstücksdienst', 1), - Dienst('P', 'Putznotdienst', 1), - Dienst('E', 'Essensausgabenotdienst', 1), - Dienst('K', 'Kochen', 1), - Dienst('A', 'Elternabend', 2) + Dienst('F', 'Frühstücksdienst', 1, aufwand=3), + Dienst('P', 'Putznotdienst', 1, aufwand=1), + Dienst('E', 'Essensausgabenotdienst', 1, aufwand=1), + Dienst('K', 'Kochen', 1, aufwand=3), + Dienst('A', 'Elternabend', 2, aufwand=2) ] # Datenstrukturen diff --git a/elterndienstplaner.py b/elterndienstplaner.py index 5539d59..0d5ae38 100755 --- a/elterndienstplaner.py +++ b/elterndienstplaner.py @@ -8,6 +8,7 @@ Datum: Dezember 2025 import sys import pulp +import multiprocessing from datetime import timedelta, date from collections import defaultdict from typing import Dict, List, Tuple, DefaultDict, Optional @@ -355,14 +356,16 @@ class Elterndienstplaner: lowBound=0) + # Zähle tatsächliche Dienste gewichtet mit dem Aufwand des Dienstes tatsaechliche_dienste_gesamt = pulp.lpSum( - x[eltern, tag, dienst] + dienst.aufwand * x[eltern, tag, dienst] for tag in self.daten.planungszeitraum for dienst in self.daten.dienste if (eltern, tag, dienst) in x ) - ziel_gesamt = sum(ziel_dienste[eltern][dienst] for dienst in self.daten.dienste) + # Zielgesamt ebenfalls mit Dienst-Aufwand gewichtet + ziel_gesamt = sum(ziel_dienste[eltern][dienst] * dienst.aufwand for dienst in self.daten.dienste) prob += (tatsaechliche_dienste_gesamt - ziel_gesamt <= fairness_abweichung_gesamt[eltern]) @@ -393,21 +396,23 @@ class Elterndienstplaner: for eltern in self.daten.eltern: for dienst in self.daten.dienste: - objective_terms.append(gewicht_f1 * fairness_abweichung_global[eltern, dienst]) - objective_terms.append(gewicht_f2 * fairness_abweichung_lokal[eltern, dienst]) + # Skaliere diensttyp-spezifische Fairness mit dem Aufwand des Dienstes + objective_terms.append(gewicht_f1 * fairness_abweichung_global[eltern, dienst] * dienst.aufwand) + objective_terms.append(gewicht_f2 * fairness_abweichung_lokal[eltern, dienst] * dienst.aufwand) + # Gesamt-Fairness (bereits dienstabhängig in den Constraints) — keine zusätzliche Mean-Skalierung mehr objective_terms.append(gewicht_f3_global * fairness_abweichung_gesamt_global[eltern]) objective_terms.append(gewicht_f4_lokal * fairness_abweichung_gesamt_lokal[eltern]) - # P1: Bevorzugte Dienste + # P1: Bevorzugte Dienste (stärker für aufwändigere Dienste) for (eltern, tag, dienst), praef in self.daten.praeferenzen.items(): if (eltern, tag, dienst) in x and praef == 1: - objective_terms.append(-5 * x[eltern, tag, dienst]) + objective_terms.append(-10 * dienst.aufwand * x[eltern, tag, dienst]) - # P2: Abgelehnte Dienste + # P2: Abgelehnte Dienste (stärker für aufwändigere Dienste) for (eltern, tag, dienst), praef in self.daten.praeferenzen.items(): if (eltern, tag, dienst) in x and praef == -1: - objective_terms.append(25 * x[eltern, tag, dienst]) + objective_terms.append(20 * dienst.aufwand * x[eltern, tag, dienst]) if objective_terms: prob += pulp.lpSum(objective_terms) @@ -480,13 +485,15 @@ class Elterndienstplaner: solver = None try: - print("Versuche CBC Solver...") - solver = pulp.PULP_CBC_CMD(msg=0, timeLimit=60) - except: + cpu_count = multiprocessing.cpu_count() + threads = max(1, cpu_count - 1) + print(f"Versuche CBC Solver mit {threads} Threads...") + solver = pulp.PULP_CBC_CMD(msg=0, timeLimit=20, threads=threads) + except Exception: try: print("Versuche GLPK Solver...") solver = pulp.GLPK_CMD(msg=0) - except: + except Exception: print("Kein spezifizierter Solver verfügbar, verwende Standard.") solver = None