Loesungs-Dedup: getGruppenId/getPersonenId, keine doppelten Sitzordnungen

- getGruppenId (Gruppen-Ebene, personen-unabhaengig: welche Gruppen mit
  welcher Anzahl je Tisch) und getPersonenId (Personen-Ebene: alphabetische
  Namen je Tisch) an Sitzplatzverteilung; Basis-Methoden an Loesung. Gleiche
  Gruppen-Verteilung -> gleiche getGruppenId und damit gleiche Wertigkeit,
  egal welche Personen die geteilten Plaetze einnehmen.
- Farm verwirft nach jeder Erzeugung/Mutation Loesungen mit schon gesehener
  getGruppenId, so dass dieselbe Sitzordnung nicht mehrfach gehalten und
  bewertet wird (defensiv: Klassen ohne getGruppenId bleiben unberuehrt,
  Tests unveraendert).
- Web: Varianten-Vergleich ueber getGruppenId statt der person-basierten
  _Signatur; gruppenId und personenId zusaetzlich im Ergebnis-JSON.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-07-15 18:10:23 +02:00
parent b29b3b22d4
commit d4844ded67
3 changed files with 80 additions and 16 deletions
+32 -1
View File
@@ -61,6 +61,15 @@ class Loesung:
print(" lade Loesung " + repr(self.Id))
def speichern(self):
print(" speichere Loesung " + repr(self.Id))
def getGruppenId(self):
"""Id fuer den Dedup-Vergleich (Basis: jede Loesung eigenstaendig).
Unterklassen ueberladen dies mit einer inhaltlichen Signatur, so
dass gleiche Loesungen dieselbe Id bekommen und nicht mehrfach
gehalten werden."""
return id(self)
def getPersonenId(self):
"""feinere Id fuer wirklich identische Loesungen (Basis: eigenstaendig)"""
return id(self)
def __repr__(self):
return ' L '+ repr(self.Id)
def __eq__(self,other):
@@ -493,6 +502,9 @@ class Farm:
self.Abstammung = Abstammung()
self._AbsZaehler = 0 # vergibt fortlaufende Loesungs-Ids 1..N
self._Generation = 0
# schon gesehene Loesungs-Signaturen (getGruppenId), damit dieselbe
# Sitzordnung nicht mehrfach gehalten und bewertet wird.
self._GeseheneGruppenIds = set()
if getattr(Zyklus, 'Modus', 'statisch') == 'adaptiv':
self._LaufAdaptiv(Klasse, *Arguments, **keywords)
else:
@@ -580,10 +592,27 @@ class Farm:
self._AbsZaehler = self._AbsZaehler + 1
return self._AbsZaehler
def _LoesungIstNeu(self, L):
"""Kontrolle nach jeder Erzeugung: True, wenn die Loesung eine bisher
ungesehene Sitzordnung hat (Vergleich ueber getGruppenId, also auf
Gruppen-Ebene) - und merkt sie dann vor. Duplikate (gleiche Sitz-
ordnung) werden so nicht noch einmal gehalten und bewertet.
Loesungen ohne getGruppenId gelten immer als neu (kein Dedup)."""
Hole = getattr(L, 'getGruppenId', None)
if Hole is None:
return True
Gid = Hole()
if Gid in self._GeseheneGruppenIds:
return False
self._GeseheneGruppenIds.add(Gid)
return True
def LoesungenErzeugen(self, N, Klasse, *Arguments, **keywords):
""" Erzeuge Loesungen"""
""" Erzeuge Loesungen (bereits gesehene Sitzordnungen werden verworfen)"""
for i in range(N):
L = Klasse( *Arguments, **keywords )
if not self._LoesungIstNeu(L):
continue # gleiche Sitzordnung schon vorhanden -> nicht behalten
L._absid = self._NeueId() # Wurzel: kein Vorgaenger
self.Abstammung.erfassen(
L._absid, None, self._Generation, getattr(L, 'value', 0), 'e' )
@@ -603,6 +632,8 @@ class Farm:
for L in chain(self.Pool):
O = deepcopy(L)
Kind = O.mutieren()
if not self._LoesungIstNeu(Kind):
continue # gleiche Sitzordnung schon gesehen -> verwerfen
# Kind bekommt eine neue fortlaufende Nummer; der Vorgaenger
# (Eltern-Id) wird separat in der Abstammung gemerkt.
ElternId = getattr(L, '_absid', None)